[OC Runtime編程指南_翻譯]六帅矗、消息轉(zhuǎn)發(fā)

[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

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ī)范咬像。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市生宛,隨后出現(xiàn)的幾起案子县昂,更是在濱河造成了極大的恐慌,老刑警劉巖陷舅,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件倒彰,死亡現(xiàn)場離奇詭異,居然都是意外死亡莱睁,警方通過查閱死者的電腦和手機(jī)待讳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門芒澜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人创淡,你說我怎么就攤上這事痴晦。” “怎么了辩昆?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵阅酪,是天一觀的道長。 經(jīng)常有香客問我汁针,道長术辐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任施无,我火速辦了婚禮辉词,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘猾骡。我一直安慰自己瑞躺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布兴想。 她就那樣靜靜地躺著幢哨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪嫂便。 梳的紋絲不亂的頭發(fā)上捞镰,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音毙替,去河邊找鬼岸售。 笑死,一個(gè)胖子當(dāng)著我的面吹牛厂画,可吹牛的內(nèi)容都是我干的凸丸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼袱院,長吁一口氣:“原來是場噩夢啊……” “哼屎慢!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起坑填,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤抛人,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后脐瑰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡廷臼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年苍在,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绝页。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡寂恬,死狀恐怖续誉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情初肉,我是刑警寧澤酷鸦,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站牙咏,受9級(jí)特大地震影響臼隔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜妄壶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一摔握、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧丁寄,春花似錦氨淌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至屑埋,卻和暖如春豪筝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背雀彼。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國打工壤蚜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人徊哑。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓袜刷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親莺丑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子著蟹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容