[OC Runtime編程指南_翻譯]一偎肃、介紹
[OC Runtime編程指南_翻譯]二、運(yùn)行時(shí)版本和平臺(tái)
[OC Runtime編程指南_翻譯]三浑此、與運(yùn)行時(shí)交互
[OC Runtime編程指南_翻譯]四累颂、消息傳遞
[OC Runtime編程指南_翻譯]五、動(dòng)態(tài)方法解析
[OC Runtime編程指南_翻譯]六凛俱、消息轉(zhuǎn)發(fā)
[OC Runtime編程指南_翻譯]七紊馏、類型編碼
[OC Runtime編程指南_翻譯]八、聲明屬性注:pdf翻譯文檔百度云下載鏈接蒲犬,密碼:zcs2
向不處理該消息的對(duì)象發(fā)送消息是錯(cuò)誤的朱监。但是,在宣布錯(cuò)誤之前原叮,運(yùn)行時(shí)系統(tǒng)會(huì)給接收對(duì)象第二次處理消息的機(jī)會(huì)
赫编。
轉(zhuǎn)發(fā)
如果將消息發(fā)送到不處理該消息的對(duì)象,則在宣布錯(cuò)誤之前奋隶,運(yùn)行時(shí)會(huì)向該對(duì)象發(fā)送一個(gè)forwardInvocation:message
擂送,其中NSInvocation
對(duì)象作為其唯一參數(shù),NSInvocation
對(duì)象將封裝原始消息及其傳遞的參數(shù)唯欣。
您可以實(shí)現(xiàn)fforwardInvocation:
方法來給消息提供默認(rèn)響應(yīng)
嘹吨,或者以其他方式避免錯(cuò)誤。顧名思義黍聂,forwardInvocation:通常用于將消息轉(zhuǎn)發(fā)到另一個(gè)對(duì)象
躺苦。
要了解轉(zhuǎn)發(fā)的范圍和意圖,請?jiān)O(shè)想以下場景:首先产还,假設(shè)您正在設(shè)計(jì)一個(gè)可以響應(yīng)名為negotiate的消息的對(duì)象匹厘,并且希望其響應(yīng)包含另一種對(duì)象的響應(yīng)。通過將協(xié)商消息傳遞給所實(shí)現(xiàn)的協(xié)商方法主體中的其他對(duì)象脐区,可以很容易地完成此操作愈诚。
更進(jìn)一步,假設(shè)您希望對(duì)象對(duì)negotiate消息的響應(yīng)與在另一個(gè)類中實(shí)現(xiàn)的響應(yīng)完全相同。實(shí)現(xiàn)這一點(diǎn)的一種方法是讓您的類從另一個(gè)類繼承該方法炕柔。然而酌泰,這樣安排事情可能是不可能的。您的類和實(shí)現(xiàn)negotiate的類位于繼承層次結(jié)構(gòu)的不同分支中可能有很好的原因匕累。
即使您的類不能繼承negotiate
方法陵刹,您仍然可以通過實(shí)現(xiàn)該方法的一個(gè)版本來“借用”該方法
,該方法只需將消息傳遞給另一個(gè)類的實(shí)例:
- (id)negotiate
{
if ( [someOtherObject respondsTo:@selector(negotiate)] )
return [someOtherObject negotiate];
return self;
}
這種方式可能會(huì)有點(diǎn)麻煩欢嘿,特別是如果有很多消息需要對(duì)象傳遞給另一個(gè)對(duì)象衰琐。你必須實(shí)現(xiàn)一個(gè)方法來覆蓋你想從另一個(gè)類借用的每個(gè)方法。而且炼蹦,你可能不想知道你在哪里寫了完整的代碼羡宙。該集合可能依賴于運(yùn)行時(shí)的事件,并且在將來實(shí)現(xiàn)新方法和類時(shí)可能會(huì)發(fā)生變化掐隐。
forwardInvocation
提供的第二個(gè)機(jī)會(huì)是:message為這個(gè)問題提供了一個(gè)不那么特別的解決方案狗热,而且是動(dòng)態(tài)的
,而不是靜態(tài)的虑省。它的工作原理
是這樣的:當(dāng)一個(gè)對(duì)象因?yàn)闆]有與消息中的選擇器匹配的方法而無法響應(yīng)消息時(shí)匿刮,運(yùn)行時(shí)系統(tǒng)通過發(fā)送 forwardInvocation:message 通知對(duì)象。
每個(gè)對(duì)象都從 NSObject
類繼承一個(gè) forwardInvocation:
方法慷妙。但是僻焚,NSObject的方法版本只是調(diào)用doesNotRecognizeSelector:
允悦。通過重寫NSObject的版本并實(shí)現(xiàn)自己的版本膝擂,您可以利用forwardInvocation:message
提供的機(jī)會(huì)將消息轉(zhuǎn)發(fā)到其他對(duì)象
。
若要轉(zhuǎn)發(fā)消息隙弛,forwardInvocation:方法只需:
- 確定消息的位置
- 并將位置與原始消息一起發(fā)送到那里
可以使用invokeWithTarget:方法發(fā)送消息:
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([someOtherObject respondsToSelector:
[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
所轉(zhuǎn)發(fā)的消息的返回值返回給原始發(fā)送方
架馋。所有類型的返回值都可以傳遞給發(fā)送方,包括id全闷、結(jié)構(gòu)和雙精度浮點(diǎn)數(shù)叉寂。
forwardInvocation:
方法可以充當(dāng)未識(shí)別消息的分發(fā)中心
,將它們分發(fā)給不同的接收方
总珠∑流ⅲ或者它可以是一個(gè)中轉(zhuǎn)站,將所有消息發(fā)送到同一個(gè)目的地
局服。它可以將一條消息轉(zhuǎn)換成另一條消息钓瞭,或者簡單地“吞下”一些消息,這樣就不會(huì)有響應(yīng)淫奔,也不會(huì)出錯(cuò)山涡。forwardInvocation:
方法還可以將多個(gè)消息合并到單個(gè)響應(yīng)中。forwardInvocation:做什么取決于實(shí)現(xiàn)者
。然而鸭丛,它為鏈接轉(zhuǎn)發(fā)鏈中的對(duì)象提供了機(jī)會(huì)竞穷,為程序設(shè)計(jì)提供了可能性。
注意:forwardInvocation:方法只有在消息沒有調(diào)用名義接收方中的現(xiàn)有方法時(shí)才能處理它們
鳞溉。例如瘾带,如果您希望您的對(duì)象將negotiate消息轉(zhuǎn)發(fā)到另一個(gè)對(duì)象,則它不能有自己的negotiate方法熟菲。如果是這樣月弛,消息就永遠(yuǎn)不會(huì)到達(dá)forwardInvocation:.。
有關(guān)轉(zhuǎn)發(fā)和調(diào)用的更多信息科盛,請參閱《基礎(chǔ)框架參考》中的NSInvocation類規(guī)范帽衙。
轉(zhuǎn)發(fā)和多重繼承
轉(zhuǎn)發(fā)模擬繼承,可以用于將多重繼承的一些效果借給Objective-C程序贞绵。如圖5-1所示厉萝,通過轉(zhuǎn)發(fā)消息來響應(yīng)消息的對(duì)象似乎借用或“繼承”在另一個(gè)類中定義的方法實(shí)現(xiàn)。
Figure 5-1 Forwarding
在本例中榨崩,Warrior(戰(zhàn)士)類的一個(gè)實(shí)例將negotiate消息轉(zhuǎn)發(fā)給Diplomat(外交官)類的實(shí)例谴垫。這位戰(zhàn)士看起來像個(gè)外交官一樣談判。它似乎對(duì)negotiate信息做出了回應(yīng)母蛛,而且出于所有實(shí)際目的翩剪,它確實(shí)做出了回應(yīng)(盡管這項(xiàng)工作實(shí)際上是一名外交官在做)。
因此彩郊,轉(zhuǎn)發(fā)消息的對(duì)象從繼承層次結(jié)構(gòu)的兩個(gè)分支“繼承”方法它自己的分支
和響應(yīng)消息的對(duì)象的分支
前弯。在上面的例子中,Warrior類似乎繼承了depolator以及它自己的超類秫逝。
轉(zhuǎn)發(fā)提供了您通常希望從多重繼承中獲得的大多數(shù)功能恕出。然而,兩者之間有一個(gè)重要的區(qū)別
:多重繼承在一個(gè)對(duì)象中結(jié)合了不同的功能违帆。它傾向于大的浙巫,多方面的對(duì)象
。另一方面刷后,轉(zhuǎn)發(fā)將單獨(dú)的責(zé)任分配給不同的對(duì)象的畴。它將問題分解為更小的對(duì)象,但以對(duì)消息發(fā)送者透明的方式關(guān)聯(lián)這些對(duì)象
尝胆。
代理對(duì)象
轉(zhuǎn)發(fā)不僅模擬多重繼承丧裁,還可以開發(fā)表示或“覆蓋”更重要對(duì)象的輕量級(jí)對(duì)象。代理代表另一個(gè)對(duì)象
并將消息傳遞給它
班巩。
在 _Objective-C _編程語言的“遠(yuǎn)程消息傳遞”中討論的代理就是這樣一個(gè)代理渣慕。代理負(fù)責(zé)將消息轉(zhuǎn)發(fā)到遠(yuǎn)程接收方的管理細(xì)節(jié)嘶炭,確保參數(shù)值在連接中被復(fù)制和檢索,等等逊桦。但它不嘗試做其他事情眨猎;它不復(fù)制遠(yuǎn)程對(duì)象的功能,而只是給遠(yuǎn)程對(duì)象一個(gè)本地地址强经,一個(gè)它可以在另一個(gè)應(yīng)用程序中接收消息的地方睡陪。
其他類型的代理對(duì)象也是可能的。例如匿情,假設(shè)您有一個(gè)對(duì)象可以處理大量數(shù)據(jù)—可能它會(huì)創(chuàng)建一個(gè)復(fù)雜的圖像或讀取磁盤上文件的內(nèi)容兰迫。設(shè)置此對(duì)象可能很耗時(shí),因此您更喜歡在真正需要它或系統(tǒng)資源暫時(shí)空閑時(shí)懶洋洋地進(jìn)行設(shè)置炬称。同時(shí)汁果,為了使應(yīng)用程序中的其他對(duì)象正常工作,您至少需要此對(duì)象的占位符玲躯。
在這種情況下据德,您可以首先創(chuàng)建
,而不是完整的對(duì)象跷车,而是它的輕量級(jí)代理
棘利。這個(gè)對(duì)象可以自己做一些事情,比如回答有關(guān)數(shù)據(jù)的問題朽缴,但大多數(shù)情況下善玫,它只會(huì)為較大的對(duì)象保留一個(gè)位置,當(dāng)時(shí)間到了密强,它會(huì)將消息轉(zhuǎn)發(fā)給它茅郎。當(dāng)代理的forwardInvocation:
方法第一次接收到發(fā)送給另一個(gè)對(duì)象的消息時(shí),它將確保該對(duì)象存在誓斥,如果不存在
只洒,則會(huì)創(chuàng)建
該對(duì)象。較大對(duì)象的所有消息都經(jīng)過代理項(xiàng)劳坑,因此,就程序的其余部分而言成畦,代理項(xiàng)和較大對(duì)象將是相同的距芬。
轉(zhuǎn)發(fā)和繼承
雖然轉(zhuǎn)發(fā)模仿繼承,但NSObject
類從不混淆兩者循帐。像respondsToSelector:
和isKindOfClass:
這樣的方法只查看繼承層次結(jié)構(gòu)框仔,而不查看轉(zhuǎn)發(fā)鏈
。例如拄养,如果一個(gè)Warrior對(duì)象被詢問是否響應(yīng)協(xié)商消息离斩,
if ( [aWarrior respondsToSelector:@selector(negotiate)] )
...
答案是否定的银舱,盡管它可以毫無差錯(cuò)地接收到談判信息,并在某種意義上通過將其轉(zhuǎn)發(fā)給外交官來作出回應(yīng)跛梗。(見圖5-1)
在許多情況下寻馏,NO是正確的答案。但也可能不是核偿。如果使用轉(zhuǎn)發(fā)來設(shè)置代理對(duì)象或擴(kuò)展類的功能诚欠,則轉(zhuǎn)發(fā)機(jī)制應(yīng)該與繼承一樣透明
。如果您希望對(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:,instanceRespondToSelector:方法還應(yīng)鏡像轉(zhuǎn)發(fā)算法尼荆。如果使用協(xié)議
左腔,conformsToProtocol:
方法也應(yīng)該添加到列表
中。類似地捅儒,如果一個(gè)對(duì)象轉(zhuǎn)發(fā)它接收到的任何遠(yuǎn)程消息翔悠,它應(yīng)該有一個(gè)methodSignatureForSelector:
的版本,該版本可以返回對(duì)轉(zhuǎn)發(fā)消息做出最終響應(yīng)的方法的準(zhǔn)確描述野芒;例如蓄愁,如果一個(gè)對(duì)象能夠?qū)⑾⑥D(zhuǎn)發(fā)到其代理項(xiàng),則可以實(shí)現(xiàn)methodSignatureForSelector:如下所示:
- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature* signature = [super methodSignatureForSelector:selector];
if (!signature) {
signature = [surrogate methodSignatureForSelector:selector];
}
return signature;
}
您可以考慮將轉(zhuǎn)發(fā)算法放在私有代碼中的某個(gè)地方狞悲,并將所有這些方法forwardInvocation:included撮抓,調(diào)用它。
注意:這是一種先進(jìn)的技術(shù)摇锋,只適用于沒有其他解決方案的情況丹拯。它不是用來替代繼承的。如果必須使用此技術(shù)荸恕,請確保完全了解執(zhí)行轉(zhuǎn)發(fā)的類和要轉(zhuǎn)發(fā)到的類的行為乖酬。
本節(jié)中提到的方法在基礎(chǔ)框架參考中的 NSObject類規(guī)范中進(jìn)行了描述。有關(guān)invokeWithTarget:的信息融求,請參閱《基礎(chǔ)框架參考》中的 NSInvocation類規(guī)范咬像。