Message Forwarding
向一個未處理響應(yīng)消息的對象發(fā)送對應(yīng)的消息,會發(fā)生錯誤。但是,runtime會給這個對象第二次機(jī)會來處理這個message,避免發(fā)生錯誤。
Forwarding(消息轉(zhuǎn)發(fā))
向一個未處理響應(yīng)消息的對象發(fā)送對應(yīng)的消息,在通知錯誤前,runtime會向receiving object發(fā)送一個帶有NSInvocation的參數(shù)的forwardInvocation:消息,這個NSInvocation參數(shù)包裝了最初的消息(origin message)和參數(shù)。
你可以實現(xiàn)forwardInvocation:,給一個默認(rèn)恢復(fù)(default response)或用其他方法阻止錯誤通知。正如其名字暗示forwardInvocation:將消息轉(zhuǎn)發(fā)給其他對象。
為了驗證轉(zhuǎn)發(fā)(forwarding)的范圍和意圖,想象一下:首先,你創(chuàng)建了一個對象能夠響應(yīng)negotiate,并希望自己的響應(yīng)(response)包含其它對象的響應(yīng)。你在negotiate的實現(xiàn)中,把negotiate消息發(fā)送給其他對象。
更近一步:讓自己對negotiate的響應(yīng)由另一個類的實現(xiàn)去響應(yīng)。一種方法是讓你的類繼承另一個類的方法,然后這是不可能的。因為不同的類可能在繼承結(jié)構(gòu)中的不同分支上。
盡管你不可以繼承另一個類的方法,但是你仍然可以借它(borrow it):把message發(fā)送給其他對象。
- (id)negotiate {
if ([someObject responseTo:@selector(negotiate)]) {
return [someObject negotiate];
}
return self;
}
以上方法可能有一些笨重:你需要實現(xiàn)方法去覆蓋所有你借的方法,對于整個消息集合(the full set of messages)你可能覆蓋不了。再一個,在運行期,這些消息集合可能會改變的。
forwardInvocation:可以減少實現(xiàn)方法,并且是動態(tài)的。它是這樣工作的:當(dāng)一個object不可以response一個message的時候,runtime會發(fā)送forwardInvocation:message給object。每一個object繼承NSObject的forwardInvocations:,可以重寫(override)將messages發(fā)送給其他對象。NSObject的forwardInvocation:簡單的調(diào)用doesNotRecognizeSelector:。
- (void)forwardInvocation:(NSInvocation *)anInvocation {
if ([someOtherObject responseSelector:[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
被轉(zhuǎn)發(fā)的消息的返回值,會被返回最初的發(fā)送者(sender)
forwardInvocation:有這么幾個角色:
- 未識別消息(unrecognized messages)的分發(fā)中心(distribution center),打包messages給接受者(receiver)。
- 或者是一個轉(zhuǎn)移站(transfer station),把所有messages發(fā)送給相同的目的地。
- 或者是把一個message轉(zhuǎn)換為另一個message,或者簡單的“吞咽”(swallow)一些messsages,所以沒有response和error。
- 或者把多個消息合并(consolidate)為一個響應(yīng)。
forwardInvocation:能做什么取決于(up to)實現(xiàn)者(implementor)。
note:只有在不能調(diào)用名義上的(nominal)receiver的存在的方法,forwardInvocation:才起作用。如果你自己可以實現(xiàn)響應(yīng)的方法,message永遠(yuǎn)不會到達(dá)forwardInvocation:
如果想了解更多forward,invocation,可以參考NSInvocation規(guī)格說明。
Forwarding and Multiple Inheritance(消息轉(zhuǎn)發(fā)和多重繼承)

消息轉(zhuǎn)發(fā)機(jī)制與多重繼承:
- 消息轉(zhuǎn)發(fā)模擬多重繼承,起到了和多重繼承一樣的效果。一個對象通過轉(zhuǎn)發(fā)機(jī)制response to message,就像“borrow”或“inherit”其他類定義的方法。
- 本來object1要響應(yīng)的message,通過borrow object2的方法來響應(yīng)message,仿佛(seem)是自己響應(yīng)一樣。
- object1和object2處在繼承結(jié)構(gòu)(inheritance hierarchy)的兩個分支(two branches)上,消息響應(yīng)讓object1看起來像是繼承自object2一樣。
- 消息轉(zhuǎn)發(fā)機(jī)制雖然提供了多重繼承的大部分功能,但是兩者仍然有一些重要的不同。多重繼承把不同的能力結(jié)合到一個單一對象中,趨向于更大,更多面的對象;另一方面,消息轉(zhuǎn)發(fā)把問題分解成更小的對象(smaller objects),但是能夠以一種對發(fā)送方(sender)透明的方式把這些對象(objects)聯(lián)系在一起。
Surrogate Objects(代理對象)
消息轉(zhuǎn)發(fā)不僅僅模擬多重繼承,它也可以創(chuàng)建輕量級的對象來代表(reperent)和覆蓋(cover)實質(zhì)的(substantial)對象。代理人(surrogate)作為其他對象的代理,并把消息幾種發(fā)給它。
假設(shè)一下:你有一個object要處理大量的數(shù)據(jù),比如:處理圖片,或讀取文件在磁盤上的內(nèi)容。建立這個對象是很耗時的,所以你更希望在需要它的時候,或系統(tǒng)不忙的時候創(chuàng)建它。但同時,你又需要它作為占位符,以便程序中的其他對象可以正常工作。
在這種情況下,你初始化一個輕量級的代理(lightweight surrogate),而不是一個完整的object。當(dāng)時間到來,把messages轉(zhuǎn)發(fā)給surrogate。
Forwarding and Inheritance(消息轉(zhuǎn)發(fā)和繼承)
盡管forwarding模擬Inheritance,但是NSObject從來不會混淆(confuse)它們。方法例如:responseSelector:和isKindOfClass:只會在Inheritance hierarchy查找,不會在消息轉(zhuǎn)發(fā)鏈中(forwarding chain)查找。
a Warrior object is asked whether it responds to negotiate message,the answer is NO, even though it can receive negotiate messages without error and respond to them, in a sense, by forwarding them to a Diplomat. (See Figure 5-1.)
if ([aWarrior responseToSelector:@selector(negotiate)])
...
在很多情況下,NO是正確的答案。但是,它也可能不是。如果你想通過forwarding去創(chuàng)建(set up)一個代理對象(surrogate object)
,或擴(kuò)展(extend)一個類的能力(capabilities),消息轉(zhuǎn)發(fā)機(jī)制(forwarding mechanism)應(yīng)該像hierarchy那樣透明(transparent)。如果你想讓你的objects看起來繼承了其他objects(收到轉(zhuǎn)發(fā)消息的objects)的行為,你需要重寫responseToSelector:和isKindOfClass:,方法中含轉(zhuǎn)發(fā)的規(guī)則(forwarding algorithm)。
- (BOOL)responseToSelector:(SEL)selector {
if ([super responseToSelector:selector]) {
return YES;
} else {
/* Here, test whether the selector message can
be forwarded to another object and whether that
object can respond to it. Return YES if it can.
*/
}
}
除了responseToSelector:和 isKindOfClass:,instancesRespondToSelector:也應(yīng)該反映(mirror)到forwarding algorithm。如果使用了協(xié)議(protocols),conformsToProtocol:也應(yīng)該加入到列表里。類似,an object轉(zhuǎn)發(fā)收到的remote messages,methodSignatureForSelector:要返回這個方法的描述。
- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature* signature = [super methodSignatureForSelector:selector];
if (!signature) {
signature = [surrogate methodSignatureForSelector:selector];
}
return signature;
}
你可以把forwarding algorithm放在私密代碼里。
note:這是很先進(jìn)的(advanced)技術(shù)(technique),但是當(dāng)其他方法無法解決問題的時候,再使用它。forwarding mechanism并不打算代替inheritance。如果你確定使用該技術(shù)時,你一定要完全理解 the behavior of the class doing the forwarding and the class you’re forwarding to.
這一節(jié)提及到的方法,在NSObject規(guī)范(specification)提及。invokeWithTarget:在NSInvocation中提及。