iOS 運(yùn)行時(shí)
Objective-C語(yǔ)言進(jìn)可能將許多決策從編譯和鏈接延緩到運(yùn)行時(shí)。它盡可能的動(dòng)態(tài)處理事務(wù)溶耘。這意味著Objective-C不僅需要編譯器還需要執(zhí)行編譯代碼的運(yùn)行時(shí)系統(tǒng)澜倦。運(yùn)行時(shí)系統(tǒng)充當(dāng)Objective-C的一種操作系統(tǒng)桩卵,使之正常工作高职。
Objective-C 程序與運(yùn)行時(shí)系統(tǒng)的交互主要在三個(gè)不同的層次
通過(guò)Objective-C 源代碼怔锌;通過(guò)基礎(chǔ)框架中NSObject類(lèi)中定義的方法寥粹;通過(guò)直接調(diào)用運(yùn)行時(shí)函數(shù)。
- Objective-C 源代碼
在大多數(shù)情況下埃元,運(yùn)行時(shí)系統(tǒng)自動(dòng)在后臺(tái)工作涝涤。只通過(guò)編寫(xiě)和編譯Objective-C源代碼才會(huì)使用到。
當(dāng)編譯包含Objective-C類(lèi)和方法的代碼岛杀,編譯器會(huì)創(chuàng)建數(shù)據(jù)結(jié)構(gòu)和函數(shù)調(diào)用阔拳,實(shí)現(xiàn)語(yǔ)言的動(dòng)態(tài)特性。數(shù)據(jù)結(jié)構(gòu)捕獲類(lèi)和分類(lèi)定義以及協(xié)議中聲明的信息楞件,其中包括Objective-C編程語(yǔ)言中定義類(lèi)和協(xié)議中討論的類(lèi)和協(xié)議衫生,也包括方法選擇器裳瘪、實(shí)例變量模板和從源碼中提取的其他信息土浸。運(yùn)行時(shí)函數(shù)的主要功能是發(fā)送消息。被源碼消息表達(dá)式調(diào)用彭羹。 - NSObject 方法
Cocoa中大多數(shù)對(duì)象都是NSObject的之類(lèi)黄伊,所以大多數(shù)對(duì)象都繼承NSObject定義的方法。(NSProxy類(lèi)是個(gè)例外派殷,更多信息參見(jiàn)消息轉(zhuǎn)發(fā)还最。)其方法因此為每個(gè)實(shí)例每個(gè)類(lèi)對(duì)象建立行為。然而毡惜,在一些情況下拓轻,NSObject類(lèi)只定義了一個(gè)模板,告知如何完成经伙,并沒(méi)有提供必要的代碼扶叉。
例如,NSObject類(lèi)定義了一個(gè)描述實(shí)例方法,該方法返回一個(gè)字符串描述類(lèi)的內(nèi)容枣氧。這主要用于調(diào)試溢十,GDB打印對(duì)象命令打印該方法返回的字符串。NSObject方法的實(shí)現(xiàn)不知道類(lèi)包含的內(nèi)容达吞,所以它返回的是一個(gè)字符串對(duì)象的名稱(chēng)和地址张弛。NSObject的之類(lèi)可以實(shí)現(xiàn)這個(gè)方法并返回更多詳情。例如酪劫,基礎(chǔ)類(lèi)NSArray返回一個(gè)列表吞鸭,包含對(duì)象的表述。
NSObject的一些方法可以簡(jiǎn)單的查詢(xún)運(yùn)行時(shí)系統(tǒng)信息覆糟。這些方法運(yùn)行對(duì)著執(zhí)行自省瞒大。例子中的這種方法是類(lèi)放,訪問(wèn)一個(gè)對(duì)象來(lái)確定它的類(lèi)搪桂;isKindOfClass:和isMemberOfClass:測(cè)試對(duì)象在繼承層次結(jié)構(gòu)中的位置透敌;respondsToSelector:表明一個(gè)對(duì)象是否可以接收特定消息;conformsToProtocol:表明一個(gè)對(duì)象是否要求實(shí)現(xiàn)特定協(xié)議中定義的方法踢械;methodForSelector:提供方法實(shí)現(xiàn)的地址酗电。類(lèi)似這樣的方法給對(duì)象自我反省的能力。 - 運(yùn)行時(shí)函數(shù)
運(yùn)行時(shí)系統(tǒng)是一個(gè)動(dòng)態(tài)共享庫(kù)内列,包含公共接口組成的一組函數(shù)和數(shù)據(jù)結(jié)構(gòu)撵术,其頭文件位于目錄/usr/include/objc。這些函數(shù)允許使用純C復(fù)制當(dāng)編寫(xiě)Objective-C代碼時(shí)編譯器生成的代碼话瞧。通過(guò)NSObject類(lèi)方法導(dǎo)出其他形式的基礎(chǔ)功能嫩与。這些函數(shù)可以開(kāi)發(fā)運(yùn)行時(shí)系統(tǒng)的其他接口和產(chǎn)生可以擴(kuò)大開(kāi)發(fā)環(huán)境的工具,Objective-C編程中不需要他們交排。然而划滋,一些運(yùn)行時(shí)函數(shù)有時(shí)候在編寫(xiě)Objective-C程序時(shí)很有用。所有的這些函數(shù)在Objective-C編程引用中有說(shuō)明埃篓。
消息傳遞
將消息表達(dá)式轉(zhuǎn)換成objc_msgSend函數(shù)調(diào)用处坪,以及如何通過(guò)名字引用方法。然后解釋如何利用objc_msgSend以及如何避免動(dòng)態(tài)綁定
objc_msgSend函數(shù)
-
在Objective-C中架专,直到運(yùn)行時(shí)同窘,消息才會(huì)綁定到方法的實(shí)現(xiàn)。編譯器才會(huì)轉(zhuǎn)換消息表達(dá)式部脚,
[receiver message]
-
調(diào)用消息傳遞函數(shù)objc_msgSend想邦。這個(gè)函數(shù)需要接收者和消息中提到的方法名即方法選擇器作為它的兩個(gè)主要參數(shù):
objc_msgSend(receiver, selector)
-
消息中傳入的任何參數(shù)都可以在objc_msgSend處理:
objc_msgSend(receiver, selector, arg1, arg2, ...)
消息傳遞函數(shù)支持動(dòng)態(tài)綁定:
- 首先,獲取選擇器指向的程序(方法實(shí)現(xiàn))委刘。因?yàn)橄嗤姆椒梢员徊煌念?lèi)分別實(shí)現(xiàn)丧没,獲取的具體程序取決于接收器的類(lèi)服傍。
- 然后調(diào)用程序,通過(guò)傳遞接收對(duì)象(數(shù)據(jù)指針)以及方法中指定的任何參數(shù)骂铁。
- 最后吹零,它傳遞程序返回值作為自己的返回值。
消息傳遞的關(guān)鍵在于編譯器編譯每個(gè)類(lèi)和對(duì)象的結(jié)構(gòu)拉庵。每個(gè)類(lèi)結(jié)構(gòu)包括這兩個(gè)基本要素
- 指向父類(lèi)的指針
- 調(diào)度表灿椅。這個(gè)表的記錄可以將方法選擇器與指定類(lèi)方法的地址關(guān)聯(lián)。setOrigin:: 方法的選擇器與setOrigin::地址(程序?qū)崿F(xiàn))有關(guān)钞支,display 方法的選擇器與的display 地址有關(guān)怔鳖,等等
當(dāng)創(chuàng)建一個(gè)新對(duì)象骤竹,會(huì)分配內(nèi)存并初始化實(shí)例變量瓤荔。首先兜看,對(duì)象變量是一個(gè)指向類(lèi)結(jié)構(gòu)的指針。該指針撼嗓,稱(chēng)為isa柬采,通過(guò)類(lèi),對(duì)象可以訪問(wèn)該類(lèi)和該對(duì)象繼承的所有類(lèi)且警。
注意:isa指針雖然不是語(yǔ)言嚴(yán)格意義上的一部分粉捻,但是是使用Objective-C運(yùn)行時(shí)系統(tǒng)所需的一個(gè)對(duì)象。一個(gè)對(duì)象須“等效于”結(jié)構(gòu)定義中的struct objc_object(定義于objc/objc.h)斑芜。然而肩刃,很少需要?jiǎng)?chuàng)建自己的根對(duì)象和繼承自NSObject 或NSProxy 的對(duì)象,自動(dòng)有isa變量杏头。
當(dāng)一個(gè)消息發(fā)送到一個(gè)對(duì)象盈包,消息傳遞函數(shù)遵循對(duì)象的isa指針,該指針指向類(lèi)結(jié)構(gòu)醇王,并在dispatch表中查找方法選擇器呢燥。如果不能找到選擇器,objc_msgSend則遵循指向父類(lèi)的指針并試圖在dispatch表找到選擇器厦画。一直找不到選擇器疮茄,objc_msgSend將一直查找類(lèi)的層次結(jié)構(gòu),直到NSObject類(lèi)根暑。一旦定位到選擇器,函數(shù)將調(diào)用表中的方法徙邻,并將其傳遞到接收對(duì)象的數(shù)據(jù)結(jié)構(gòu)排嫌。
運(yùn)行時(shí)選擇以這種方式實(shí)現(xiàn)方法$掷纾或者以面向?qū)ο缶幊绦g(shù)語(yǔ)來(lái)說(shuō)淳地,該方法是動(dòng)態(tài)綁定到消息怖糊。
為了加快消息傳遞過(guò)程,運(yùn)行時(shí)系統(tǒng)緩存使用的方法的選擇器和地址颇象。每個(gè)類(lèi)有一個(gè)單獨(dú)的緩存伍伤,可以包含繼承方法和類(lèi)中定義方法的選擇器。在搜索dispatch表之前遣钳,消息傳遞程序首先檢查接收對(duì)象類(lèi)(理論上扰魂,是有可能再次使用的方法)的緩存。如果方法選擇器在緩存中蕴茴,消息傳遞稍微比函數(shù)調(diào)用慢劝评。一旦一個(gè)程序運(yùn)行足夠長(zhǎng)時(shí)間來(lái)“熱身”緩存,幾乎所有發(fā)送的消息都能找到緩存方法倦淀。在程序運(yùn)行時(shí)蒋畜,緩存能動(dòng)態(tài)適應(yīng)新消息。
使用隱式參數(shù)
當(dāng)objc_msgSend發(fā)現(xiàn)實(shí)現(xiàn)方法的程序撞叽,它調(diào)用程序姻成,并傳遞消息中所有的參數(shù)。也傳遞兩個(gè)隱藏參數(shù)到程序:
- 接收對(duì)象
- 方法選擇器
這些參數(shù)為每個(gè)方法實(shí)現(xiàn)提供明確信息愿棋,這些信息關(guān)于調(diào)用它們的消息表達(dá)式佣渴。它們被認(rèn)為是“隱藏”的,因?yàn)榉椒ǘx代碼中未聲明它們初斑。當(dāng)編譯代碼時(shí)辛润,它們插入到實(shí)現(xiàn)中。
盡管這些參數(shù)沒(méi)有顯式的聲明见秤,源代碼仍然可以引用它們(就像它可以引用接收對(duì)象的實(shí)例變量)砂竖。方法引用接收對(duì)象作為self,以及自己的選擇器作為_(kāi)cmd鹃答。在下面的例子中乎澄,_cmd引用strange 方法的選擇器,self引用接收一個(gè)strange 消息的對(duì)象测摔。
- strange
{
id target = getTheReceiver();
SEL method = getTheMethod();
//如果調(diào)用的是自己 置济,就執(zhí)行自己,如果不是 锋八,就繼續(xù)傳遞
if ( target == self || method == _cmd )
return nil;
return [target performSelector:method];
}
Self對(duì)兩個(gè)參數(shù)更加有用浙于。實(shí)際上,接收對(duì)象的實(shí)例變量可用于方法定義挟纱。
獲取方法地址
避免動(dòng)態(tài)綁定的唯一方法是獲取方法的地址并且直接調(diào)用它羞酗,就好像它是個(gè)函數(shù)。當(dāng)一個(gè)特定的方法多次連續(xù)執(zhí)行紊服,并且你希望每次執(zhí)行該方法時(shí)避免消息傳遞開(kāi)銷(xiāo)檀轨,在這種極少數(shù)的情況下胸竞,該方法可行。
NSObject類(lèi)中定義一個(gè)methodForSelector:方法参萄,可以訪問(wèn)指向?qū)崿F(xiàn)方法程序的指針卫枝,然后使用指針調(diào)用該程序。methodForSelector:指針的返回值必須指向合適的函數(shù)類(lèi)型讹挎。必須包含返回值和參數(shù)類(lèi)型校赤。
下面的例子展示了程序如何實(shí)現(xiàn)setFilled: 方法:
void (*setter)(id, SEL, BOOL);
int i;
setter = (void (*)(id, SEL, BOOL))[target
methodForSelector:@selector(setFilled:)];
for ( i = 0 ; i < 1000 ; i++ )
setter(targetList[i], @selector(setFilled:), YES);
前兩個(gè)參數(shù)傳遞給接收對(duì)象(self)的程序和方法選擇器(_cmd)。這些參數(shù)在方法語(yǔ)法中是隱藏的淤袜,但當(dāng)該方法當(dāng)成函數(shù)調(diào)用時(shí)痒谴,必須是顯式的。
使用methodForSelector:方法避免動(dòng)態(tài)綁定節(jié)省了消息轉(zhuǎn)發(fā)所需的大部分時(shí)間铡羡。然而积蔚,只有在特定消息重復(fù)多次的情況下,如上面的for循環(huán)烦周,節(jié)省時(shí)間才會(huì)有重要意義尽爆。
注意:Cocoa運(yùn)行時(shí)系統(tǒng)提供methodForSelector:方法,該方法并不是Objective-C 語(yǔ)言本身的特性读慎。
動(dòng)態(tài)方法解析
如何動(dòng)態(tài)的提供一個(gè)方法的實(shí)現(xiàn)漱贱。
在有些情況下,需要?jiǎng)討B(tài)的提供一個(gè)方法的實(shí)現(xiàn)夭委。例如幅狮,Objective-C 聲明的屬性特征(見(jiàn)Objective-C 編程語(yǔ)言中的聲明屬性)包含@dynamic指令:
@dynamic propertyName;
它告訴編譯器,將動(dòng)態(tài)的提供該方法與屬性株灸。
可以實(shí)現(xiàn)resolveInstanceMethod: 和resolveClassMethod: 方法來(lái)動(dòng)態(tài)的提供一個(gè)給定選擇器的實(shí)例和對(duì)應(yīng)的類(lèi)方法提供實(shí)現(xiàn)崇摄。
一個(gè)Objective-C 方法僅僅是一個(gè)至少有兩個(gè)參數(shù)self和_cmd的C函數(shù)』派眨可以添加在類(lèi)中添加一個(gè)函數(shù)作為一個(gè)使用class_addMethod.函數(shù)的方法逐抑。因此,有以下函數(shù)
void dynamicMethodIMP(id self, SEL _cmd) {
// implementation ....
}
可以動(dòng)態(tài)的將它添加到類(lèi)中作為一個(gè)使用 resolveInstanceMethod: 的方法(稱(chēng)為resolveThisMethodDynamically):
@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
if (aSEL == @selector(resolveThisMethodDynamically)) {
class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:aSEL];
}
@end
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
"v@:" v的意思是參數(shù) void 后邊固定@:
參見(jiàn)文檔
動(dòng)態(tài)加載
一個(gè) Objective-C 程序可以在運(yùn)行過(guò)程中加載和鏈接新類(lèi)和分類(lèi)屹蚊。程序中納入新代碼厕氨,在開(kāi)始加載的類(lèi)和類(lèi)別中都可以使用。
動(dòng)態(tài)加載可以用來(lái)做很多不同的事情汹粤。例如命斧,系統(tǒng)偏好設(shè)置應(yīng)用程序中的模塊都是動(dòng)態(tài)加載的。
在Cocoa 環(huán)境中玄括,動(dòng)態(tài)加載通常用來(lái)自定義應(yīng)用程序冯丙。其他人可以編寫(xiě)模塊讓你的程序在運(yùn)行時(shí)加載,類(lèi)似IB加載自定義調(diào)色板和OS X系統(tǒng)設(shè)置應(yīng)用程序加載自定義偏好模塊遭京∥赶В可加載模塊可擴(kuò)展應(yīng)用程序。他們以你允許的方式貢獻(xiàn)代碼哪雕,但是不能自己預(yù)計(jì)和定義船殉。你提供框架,其他人提供代碼斯嚎。
盡管有一個(gè)運(yùn)行時(shí)函數(shù)在Mach-O 文件(在objc/objc-load.h中定義的objc_loadModules)中利虫,執(zhí)行 Objective-C 模塊的動(dòng)態(tài)加載。Cocoa的NSBundle 類(lèi)為動(dòng)態(tài)加載提供了更方便的接口堡僻,這個(gè)接口是面向?qū)ο蟛⑴c相關(guān)服務(wù)結(jié)合糠惫。了解NSBundle 類(lèi)信息和使用,可參閱基礎(chǔ)框架引用中的NSBundle 類(lèi)規(guī)范钉疫。Mach-O 文件信息可查看OS X ABI Mach-O文件格式引用硼讽。
消息轉(zhuǎn)發(fā)
發(fā)送消息到不處理該消息的對(duì)象會(huì)發(fā)生錯(cuò)誤。然而牲阁,在聲明錯(cuò)誤之前固阁,運(yùn)行時(shí)系統(tǒng)給接收對(duì)象第二次機(jī)會(huì)處理該消息。
可以借此實(shí)現(xiàn)多重繼承的功能
如果發(fā)送消息到不處理該消息的對(duì)象城菊,在聲明錯(cuò)誤之前备燃,運(yùn)行時(shí)給該對(duì)象發(fā)送forwardInvocation: 消息,NSInvocation 對(duì)象作為唯一參數(shù)凌唬。NSInvocation 對(duì)象封裝原始消息和需要傳遞的參數(shù)并齐。
可以實(shí)現(xiàn) forwardInvocation:方法,提供一個(gè)默認(rèn)消息響應(yīng)客税,或者以其他方式避免錯(cuò)誤况褪。顧名思義, forwardInvocation:通常用來(lái)將消息轉(zhuǎn)發(fā)給另一個(gè)對(duì)象
為了看到轉(zhuǎn)發(fā)的范圍和目的霎挟,想象以下場(chǎng)景:首先假設(shè)窝剖,你正在設(shè)計(jì)一個(gè)叫做negotiate的對(duì)象可以響應(yīng)消息,你希望它能響應(yīng)另一個(gè)對(duì)象的響應(yīng)酥夭。你可以通過(guò)傳遞negotiate消息到你實(shí)現(xiàn)的negotiate方法中的另一個(gè)對(duì)象赐纱。
更近一步,假設(shè)希望對(duì)象精確的響應(yīng)negotiate 消息熬北,則需要在另一個(gè)類(lèi)中實(shí)現(xiàn)疙描。實(shí)現(xiàn)這個(gè)目標(biāo)的一個(gè)方法是讓類(lèi)繼承其他類(lèi)的方法。然而讶隐,它不可能以這種方式安排事情起胰。也許存在充分的理由為什么你的類(lèi)和實(shí)現(xiàn)negotiate 的類(lèi)在不同分支的繼承層次結(jié)構(gòu)中。
即使類(lèi)不能繼承negotiate 方法巫延,仍然可以通過(guò)實(shí)現(xiàn)一個(gè)版本的方法來(lái)“借用”它效五,該方法只是簡(jiǎn)單的將信息傳遞給另一個(gè)類(lèi)的實(shí)例:
-(id)negotiate
{
if([someOtherObject respondsTo:@selector(negotiate)])
return [someOtherObject negotiate];
return self;
}
這種方式有點(diǎn)麻煩地消,特別是對(duì)象要傳遞大量消息到另一個(gè)對(duì)象。你必須實(shí)現(xiàn)一個(gè)方法來(lái)覆蓋每個(gè)從其他類(lèi)借來(lái)的方法畏妖。此外脉执,這種方法不能處理你不知道的情況。例如戒劫,在寫(xiě)代碼的時(shí)候半夷,你想轉(zhuǎn)發(fā)所有的消息。這取決于運(yùn)行時(shí)的事件迅细,有可能在將來(lái)作為新方法和類(lèi)實(shí)現(xiàn)巫橄。
消息提供第二次機(jī)會(huì),提供了一個(gè)相對(duì)不那么特殊的解決方案茵典,該方案是動(dòng)態(tài)的而非靜態(tài)的湘换。它的工作原理是:當(dāng)一個(gè)對(duì)象因?yàn)闆](méi)有一個(gè)方法匹配消息中的選擇器而無(wú)法響應(yīng)消息時(shí),運(yùn)行時(shí)系統(tǒng)通過(guò)發(fā)送一個(gè)forwardInvocation: 消息通知該對(duì)象敬尺。每個(gè)對(duì)象都從NSObject類(lèi)繼承了forwardInvocation: 方法枚尼。然而,NSObject版本的方法只是簡(jiǎn)單的調(diào)用doesNotRecognizeSelector:砂吞。通過(guò)重寫(xiě)NSObject版本署恍,實(shí)現(xiàn)自己的版本,可以利用forwardInvocation: 消息提供的機(jī)會(huì)轉(zhuǎn)發(fā)消息到其他對(duì)象蜻直。
為了轉(zhuǎn)發(fā)一條消息盯质,forwardInvocation:方法需要做的是:
確定消息要發(fā)送到哪里
發(fā)送消息原來(lái)的參數(shù)到那里
消息可以發(fā)送到 invokeWithTarget: 方法:
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([someOtherObject respondsToSelector:
[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
轉(zhuǎn)發(fā)消息的返回值返回給原始發(fā)送者。所有類(lèi)型的返回值都可以傳遞給發(fā)送者概而,包括id呼巷,結(jié)構(gòu),雙精度浮點(diǎn)數(shù)赎瑰。
forwardInvocation: 方法可以作為無(wú)法識(shí)別消息的分發(fā)中心王悍,將消息打包給不同的接收者〔吐或者可以作為中轉(zhuǎn)站压储,發(fā)送所有消息到相同的目的地。它可以把一個(gè)消息轉(zhuǎn)發(fā)給另一個(gè)源譬,或簡(jiǎn)單的“吞咽”一些消息集惋,所有沒(méi)有響應(yīng)也沒(méi)有錯(cuò)誤。forwardInvocation:方法還可以合并幾個(gè)消息到一個(gè)響應(yīng)上踩娘。forwardInvocation:做什么是由系統(tǒng)決定的刮刑。然而,它提供一個(gè)機(jī)會(huì),使得轉(zhuǎn)發(fā)鏈接中的鏈接對(duì)象為程序設(shè)計(jì)提供可能雷绢。
注意:方法只有在他們不調(diào)用名義上接收者的現(xiàn)有方法的情況下才處理消息泛烙。例如,如果你希望你的對(duì)象轉(zhuǎn)發(fā)negotiate 消息到另一個(gè)對(duì)象习寸,則對(duì)象不能有自己的negotiate 方法胶惰。如果是這樣傻工,消息永遠(yuǎn)不會(huì)到forwardInvocation:霞溪。
關(guān)于轉(zhuǎn)發(fā)和調(diào)用的更多信息,參見(jiàn)基礎(chǔ)框架引用中NSInvocation 類(lèi)規(guī)范中捆。
轉(zhuǎn)發(fā)和多重繼承
轉(zhuǎn)發(fā)提供了大部分多重繼承的功能鸯匹。然而,兩者之間有個(gè)重要的區(qū)別:多重繼承在單一對(duì)象上結(jié)合了不同的功能泄伪。它更加強(qiáng)大殴蓬,多層面對(duì)象。另一方面蟋滴,轉(zhuǎn)發(fā)分配單獨(dú)的職責(zé)給不同的對(duì)象染厅。它將問(wèn)題分解為更小的對(duì)象,但是以一種方式將對(duì)象聯(lián)合津函,該方式對(duì)消息發(fā)送者是透明的肖粮。
代理對(duì)象
轉(zhuǎn)發(fā)不僅模仿多重繼承,還可以開(kāi)發(fā)輕量級(jí)對(duì)象代表或“覆蓋”更實(shí)質(zhì)的對(duì)象尔苦。代理代替其他對(duì)象并傳送消息給它涩馆。
Objective-C 編程語(yǔ)言中“遠(yuǎn)程消息傳遞”中描述了該代理。代理負(fù)責(zé)管理消息轉(zhuǎn)發(fā)到遠(yuǎn)程接受者允坚,確保復(fù)制參數(shù)值和恢復(fù)鏈接魂那,等等。但不嘗試做其他稠项。它不與遠(yuǎn)程對(duì)象的功能重復(fù)涯雅,只是簡(jiǎn)單的給遠(yuǎn)程對(duì)象一個(gè)本地地址,該地址可以在另一個(gè)應(yīng)用程序中接收消息展运。
其他類(lèi)型的代理對(duì)象也是可以的活逆。假設(shè),如果有個(gè)對(duì)象操作大量數(shù)據(jù)乐疆,它也許會(huì)創(chuàng)建一個(gè)復(fù)雜的圖片或者讀取磁盤(pán)上的文件內(nèi)容划乖。設(shè)置該對(duì)象可能非常耗時(shí),所以為了簡(jiǎn)單挤土,只有真正需要的時(shí)候或者系統(tǒng)資源暫時(shí)閑置時(shí)使用琴庵。同時(shí),為了保證其他對(duì)象在應(yīng)用程序正常運(yùn)行,該對(duì)象至少需要一個(gè)占位符迷殿。
在這樣的情況下儿礼,你可以首先創(chuàng)建,不是成熟的對(duì)象庆寺,但是時(shí)一個(gè)輕量級(jí)的代理蚊夫。這個(gè)對(duì)象可以做些自己的事情,比如回答數(shù)據(jù)問(wèn)題懦尝,但更多的是為大對(duì)象占個(gè)位置知纷,當(dāng)時(shí)間到了轉(zhuǎn)發(fā)消息給大對(duì)象。當(dāng)代理forwardInvocation: 方法首先接收一條消息傳遞給另一個(gè)對(duì)象陵霉,它將確保對(duì)象存在琅轧,如果不存在則創(chuàng)建。所有大對(duì)象的消息都是通過(guò)代理踊挠,因此乍桂,對(duì)于其余程序而言,代理和大對(duì)象是一樣的效床。
轉(zhuǎn)發(fā)個(gè)繼承
盡管轉(zhuǎn)發(fā)模仿繼承睹酌,NSObject類(lèi)不會(huì)混淆兩者。像respondsToSelector: 方法和isKindOfClass: 方法只查看繼承層次結(jié)構(gòu)剩檀,不在轉(zhuǎn)發(fā)鏈上憋沿。
如果使用轉(zhuǎn)發(fā)來(lái)設(shè)置代理對(duì)象或擴(kuò)展一個(gè)類(lèi)的功能,轉(zhuǎn)發(fā)機(jī)制必須如同繼承一樣透明谨朝。如果想讓你的對(duì)象假裝它們真正繼承它們轉(zhuǎn)發(fā)消息的對(duì)象的行為卤妒,需要重新實(shí)現(xiàn)respondsToSelector: 方法和isKindOfClass: 方法,包括轉(zhuǎn)發(fā)算法字币。
- (BOOL)respondsToSelector:(SEL)aSelector
{
if ( [super respondsToSelector:aSelector] )
return YES;
else {
/* Here, test whether the aSelector message can *
* be forwarded to another object and whether that *
* object can respond to it. Return YES if it can. */
}
return NO;
}
除了respondsToSelector: 方法和isKindOfClass: 方法则披,instancesRespondToSelector:方法也要反應(yīng)轉(zhuǎn)發(fā)算法。如果使用協(xié)議洗出,conformsToProtocol:方法需要添加到列表中士复。同樣的,如果一個(gè)對(duì)象轉(zhuǎn)發(fā)它接收到的任何遠(yuǎn)程消息翩活,它必須有的一個(gè)methodSignatureForSelector: 版本阱洪,該版本必須可以準(zhǔn)確返回描述方法。例如菠镇,如果一個(gè)對(duì)象可以轉(zhuǎn)發(fā)消息給它的代理冗荸,需要實(shí)現(xiàn)如下methodSignatureForSelector: 方法:
- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature* signature = [super methodSignatureForSelector:selector];
if (!signature) {
signature = [surrogate methodSignatureForSelector:selector];
}
return signature;
}
注意:這是一種先進(jìn)技術(shù),只有在沒(méi)有其他解決方案可行的情況下才適用利耍。它并不打算取代繼承蚌本。如果你必須使用這種技術(shù)盔粹,確保你完全理解轉(zhuǎn)發(fā)類(lèi)和你要轉(zhuǎn)發(fā)的類(lèi)的行為。
類(lèi)型編碼
為了協(xié)助運(yùn)行時(shí)系統(tǒng)程癌,編譯器用字符串為每個(gè)方法的返回值和參數(shù)類(lèi)型和方法選擇器編碼舷嗡。使用的編碼方案在其他情況下也很有用,所以它是public 的嵌莉,可用于@encode() 編譯器指令进萄。當(dāng)給定一個(gè)類(lèi)型參數(shù),返回一個(gè)編碼類(lèi)型字符串锐峭。類(lèi)型可以是一個(gè)基本類(lèi)型如int中鼠,指針,結(jié)構(gòu)或聯(lián)合標(biāo)記只祠,或任何類(lèi)型的類(lèi)名兜蠕,事實(shí)上,都可以作為C sizeof() 運(yùn)算符的參數(shù)抛寝。
--
聲明屬性
當(dāng)編譯器遇到屬性聲明時(shí)(參見(jiàn)The Objective-C 編程語(yǔ)言中的聲明屬性),它生成與封閉類(lèi)曙旭、分類(lèi)或協(xié)議相關(guān)的描述性元數(shù)據(jù)盗舰。可以通過(guò)函數(shù)訪問(wèn)元數(shù)據(jù)桂躏,該函數(shù)支持通過(guò)類(lèi)或協(xié)議名稱(chēng)查找屬性钻趋,獲取屬性的類(lèi)型作為@encode 字符串,并復(fù)制property的屬性列表作為C字符串?dāng)?shù)組剂习。聲明的屬性列表可用于每個(gè)類(lèi)和協(xié)議蛮位。
屬性類(lèi)型和函數(shù)
屬性結(jié)構(gòu)為屬性描述符定義了一個(gè)不透明句柄。
typedef struct objc_property *Property;
可以使用class_copyPropertyList 和 protocol_copyPropertyList 函數(shù)分別檢索屬性數(shù)組和類(lèi)(包括加載的類(lèi)別)和協(xié)議:
objc_property_t *class_copyPropertyList(Class cls, unsigned intint *outCount)
objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned intint *outCount)
例如鳞绕,類(lèi)聲明如下:
@interface Lender : NSObject {
float alone;
}
@property float alone;
@end
使用如下代碼失仁,可以得到屬性列表:
id LenderClass = objc_getClass("Lender");
unsigned int outCount;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
可以使用property_getName函數(shù)來(lái)找到屬性名稱(chēng):
const charchar *property_getName(objc_property_t property)
可以使用class_getProperty和protocol_getProperty函數(shù)分別獲取類(lèi)和協(xié)議中給定名稱(chēng)的屬性的引用:
objc_property_t class_getProperty(Class cls, const charchar *name)
objc_property_t protocol_getProperty(Protocol *proto, const charchar *name, BOOL isRequiredProperty, BOOL isInstanceProperty)
可以使用property_getAttributes 函數(shù)獲取屬性的名稱(chēng)和@encode類(lèi)型字符串。編碼類(lèi)型字符串詳情可查看類(lèi)型編碼们何;字符串詳情可查看屬性類(lèi)型字符串和property屬性描述例子萄焦。
const charchar *property_getAttributes(objc_property_t property)
把這些放在一起,使用如下代碼冤竹,可以打印所有與類(lèi)關(guān)聯(lián)的屬性列表:
id LenderClass = objc_getClass("Lender");
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
}