向?qū)ο蟀l(fā)送一個(gè)無法處理的消息將會(huì)導(dǎo)致錯(cuò)誤,然而,在報(bào)告錯(cuò)誤之前,運(yùn)行時(shí)系統(tǒng)會(huì)提供給消息的接收者第二次機(jī)會(huì)去處理消息。
轉(zhuǎn)發(fā)(Forwarding)
如果你向一個(gè)無法處理某個(gè)消息的對(duì)象發(fā)送了該消息,在報(bào)錯(cuò)之前,運(yùn)行時(shí)(runtime)會(huì)向?qū)ο蟀l(fā)送forwardInvocation:消息帶有NSInvocation對(duì)象作為其唯一參數(shù)。NSInvocation對(duì)象壓縮傳遞給它的原始信息和參數(shù)
你可以實(shí)現(xiàn)forwardInvocation:方法來給消息一個(gè)默認(rèn)的回復(fù),或者通過某些方式避免報(bào)錯(cuò)。正如其方法名稱的含義,forwardInvocation:是用來將消息轉(zhuǎn)發(fā)給另一個(gè)對(duì)象
想象如下場(chǎng)景:假如首先,你設(shè)計(jì)一個(gè)對(duì)象可以響應(yīng)消息negotiate,并且你希望它的返回包含另一個(gè)對(duì)象的返回,你可以很簡(jiǎn)單的實(shí)現(xiàn)這個(gè)需求,通過在該對(duì)象的方法實(shí)現(xiàn)中傳遞negotiate消息給另一個(gè)對(duì)象并返回,更進(jìn)一步,假設(shè)我們想讓negotiate方法的返回和另一個(gè)類negotiate方法的返回完全一致。其中一種方法是通過繼承,但是這不一定是最好的方法,blabla~
即使不通過繼承的方法,我們也可以通過“借用”的方式實(shí)現(xiàn)
- (id)negotiate
{
if ( [someOtherObject respondsTo:@selector(negotiate)] )
return [someOtherObject negotiate];
return self;
}
這種做法有些笨重,尤其是如果存在很多方法想傳遞給其他的對(duì)象的時(shí)候,你必須實(shí)現(xiàn)每一個(gè)想從其他類借用的方法。更糟糕的是,這樣做可能發(fā)生意料之外的事,(Moreover, it would be impossible to handle cases where you didn’t know, at the time you wrote the code, the full set of messages you might want to forward. That set might depend on events at runtime, and it might change as new methods and classes are implemented in the future.這是文檔所說的意料之外的事,沒看太懂)
(拋磚引玉,這上面兩段是磚,這篇文檔真喜歡繞圈子,繞了一個(gè)大圈子,終于繞回來了==!)
forwardInvocation:方法為這類問題提供了專門的方法(a less ad hoc solution我也搞不清楚是專門的還是臨時(shí)的了)并且是動(dòng)態(tài)的。大致流程如下:當(dāng)一個(gè)對(duì)象因?yàn)闆]有方法匹配消息中的選擇器而不能響應(yīng)某個(gè)消息時(shí),運(yùn)行時(shí)系統(tǒng)(runtime system)會(huì)向?qū)ο蟀l(fā)送forwardInvocation:消息。每一個(gè)對(duì)象(這里的對(duì)象應(yīng)該特指繼承NSObject的對(duì)象)都會(huì)從NSObject對(duì)象繼承forwardInvocation:方法。然而NSObject類的這個(gè)方法會(huì)簡(jiǎn)單的調(diào)用doesNotRecognizeSelector:。通過重寫NSObject的方法,你就可以利用forwardInvocation:將消息轉(zhuǎn)發(fā)給其他對(duì)象
轉(zhuǎn)發(fā)消息,forwardInvocation:需要做下面的事情:
@1 決定消息的去向
@2 將消息和原始參數(shù)傳遞過去
例如 消息可以被invokeWithTarget:發(fā)送
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([someOtherObject respondsToSelector:
[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
轉(zhuǎn)發(fā)的消息的返回值會(huì)被返回給原始的發(fā)送者(sender).所有類型的返回值都可以傳遞給(sender)。(including ids, structures, and double-precision floating-point numbers.)
forwardInvocation:方法可以看作是無法識(shí)別的消息(unrecognized messages)的分發(fā)中心,將它們發(fā)送給不同的接收者?;蛘咦鳛橹修D(zhuǎn)站,將所有的消息發(fā)送給同一個(gè)目的地。它可以將一個(gè)消息轉(zhuǎn)化成另一個(gè),或者只是簡(jiǎn)單的吞噬,不返回也不報(bào)錯(cuò)。forwardInvocation:方法可以將多條消息同一個(gè)返回。forwardInvocation:的所為由該方法的實(shí)現(xiàn)過程決定。
注意:forwardInvocation:方法僅在調(diào)用了接收者不存在的方法時(shí)才會(huì)被調(diào)用。假如,你希望你的對(duì)象轉(zhuǎn)發(fā)negotiate消息,那么他就不能實(shí)現(xiàn)negotiate方法
Forwarding and Multiple Inheritance
在Objective-C程序中,轉(zhuǎn)發(fā)模仿多繼承,并且可以用來達(dá)到多繼承的一些效果。如圖所示,對(duì)象通過轉(zhuǎn)發(fā)“繼承”另一個(gè)類中的方法實(shí)現(xiàn)
Forwarding

上圖中,Warrior的一個(gè)實(shí)例轉(zhuǎn)發(fā)了negotiate消息給Diplomat實(shí)例。Warrior看上去將像Diplomat一樣處理negotiate消息。
轉(zhuǎn)發(fā)消息的對(duì)象,就像“繼承”了兩個(gè)繼承鏈。一個(gè)其本身的繼承鏈,一個(gè)消息轉(zhuǎn)發(fā)對(duì)象的繼承鏈。在上例中,Warrior看上去既繼承了自己的父類又“繼承”了Diplomat
轉(zhuǎn)發(fā)提供了類似多繼承的很多特性。然而兩者卻有很大不同:多繼承將不同的性能結(jié)合在單一對(duì)象中。導(dǎo)致龐大的,復(fù)雜的對(duì)象。相反,轉(zhuǎn)發(fā)機(jī)制,將不同的職責(zé)賦予不同的對(duì)象。將復(fù)雜問題分解成小的對(duì)象,但是又通過一種方式將這些對(duì)象結(jié)合起來,并且保持對(duì)消息發(fā)送者(sender)透明。
Surrogate Objects(代理對(duì)象)
轉(zhuǎn)發(fā)機(jī)制不僅僅模仿了多繼承,它也使得使用輕量對(duì)象代替復(fù)雜對(duì)象成為可能。代理對(duì)象代替其他對(duì)象,并且過濾消息(The surrogate stands in for the other object and funnels messages to it)。
在The Objective-C Programming Language,“Remote Messaging” 文中提到的proxy就屬于一種代理(Surrogate),proxy負(fù)責(zé)向一個(gè)遙遠(yuǎn)的接收者轉(zhuǎn)發(fā)消息的管理細(xì)節(jié)(A proxy takes care of the administrative details of forwarding messages to a remote receiver)保證參數(shù)值復(fù)制,在連接中檢索,等等。但它不會(huì)嘗試做別的事情,它不會(huì)復(fù)制接收者的函數(shù)功能,只是簡(jiǎn)單的給接收者發(fā)送一個(gè)地址,在那里可以從另一個(gè)程序接收消息(?_?)
其他類型的代理對(duì)象也是可能的。假設(shè),你有一個(gè)對(duì)象操作很多數(shù)據(jù)--假如它繪制了一個(gè)復(fù)雜的圖形或者從硬盤上讀取了一個(gè)文件的內(nèi)容。創(chuàng)建這樣一個(gè)對(duì)象需要耗費(fèi)很多時(shí)間,所以你可能會(huì)懶加載-當(dāng)真正需要的時(shí)候或者系統(tǒng)閑置的時(shí)候。這時(shí),你就需要一個(gè)placeholder代替這個(gè)對(duì)象,以使其他對(duì)象調(diào)用正常
在這種情況下,你可以首先初始化一個(gè)不完全對(duì)象,但是是一個(gè)完全對(duì)象的輕量代理。這種對(duì)象可以自己做一些事情,像根據(jù)數(shù)據(jù)回答問題,但多數(shù)情況下,它只是用來暫時(shí)替代完全對(duì)象并轉(zhuǎn)發(fā)消息給完全對(duì)象。當(dāng)代理對(duì)象的forwardInvocation:方法第一次接收消息并轉(zhuǎn)發(fā)時(shí),你就要確保接收消息的對(duì)象存在,如果不存在就創(chuàng)建。所有的消息都通過代理對(duì)象轉(zhuǎn)發(fā)給完全對(duì)象,對(duì)程序來說,代理對(duì)象和完全對(duì)象就是一個(gè)對(duì)象
Forwarding and Inheritance
盡管轉(zhuǎn)發(fā)機(jī)制模擬了繼承,而NSObject類不會(huì)分不清兩者。respondsToSelector: 和 isKindOfClass:只會(huì)在繼承鏈中查找,而不會(huì)在轉(zhuǎn)發(fā)鏈中查找,打個(gè)比方,Warrior 對(duì)象如果調(diào)用下面的方法
if ( [aWarrior respondsToSelector:@selector(negotiate)] )
...
返回值一定是NO,盡管它可以接收negotiate消息并不會(huì)報(bào)錯(cuò)同時(shí)會(huì)返回結(jié)果。見上面多繼承的圖。
在多數(shù)情況下,NO都是正確的。然而。。。如果你用轉(zhuǎn)發(fā)機(jī)制創(chuàng)建了一個(gè)代理對(duì)象(surrogate object)或者擴(kuò)展了一個(gè)類的能力。轉(zhuǎn)發(fā)機(jī)制在這時(shí)應(yīng)該和繼承一樣平等對(duì)待。如果你希望你的對(duì)象的 消息轉(zhuǎn)發(fā) 達(dá)到和 繼承 一樣的效果,你需要重寫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:方法也需要考慮轉(zhuǎn)發(fā)的情況,如果使用了協(xié)議,conformsToProtocol:也得加入考慮。相似的,如果對(duì)象轉(zhuǎn)發(fā)了接收到的遠(yuǎn)程信息,它需要實(shí)現(xiàn)methodSignatureForSelector:方法來返回準(zhǔn)確的方法描述來回應(yīng)轉(zhuǎn)發(fā)消息?例如,如果一個(gè)對(duì)象可以向代理對(duì)象轉(zhuǎn)發(fā)消息,你需要這樣實(shí)現(xiàn)
- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature* signature = [super methodSignatureForSelector:selector];
if (!signature) {
signature = [surrogate methodSignatureForSelector:selector];
}
return signature;
}
注意:這是一種先進(jìn)的技術(shù),僅僅適合在別無他法的情況下使用,不能用來代替繼承。如果你需要使用它,保證你完全明白你的類的行為和轉(zhuǎn)發(fā)的類的行為