Runtime(三)消息轉(zhuǎn)發(fā)

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ā)和多重繼承)

Figure 5-1
Figure 5-1

消息轉(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中提及。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 Runtime是iOS開發(fā)者進(jìn)階必須學(xué)習(xí)的一個知識點。網(wǎng)上關(guān)于Runtime 有許多介紹,有深入有簡單介紹,...
    獨酌丿紅顏閱讀 339評論 0 6
  • 前言 Runtime是iOS開發(fā)者進(jìn)階必須學(xué)習(xí)的一個知識點。網(wǎng)上關(guān)于Runtime 有許多介紹,有深入有簡單介紹,...
    雨田_Toping閱讀 1,626評論 1 1
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,106評論 0 9
  • 前言 Runtime是iOS開發(fā)者進(jìn)階必須學(xué)習(xí)的一個知識點。網(wǎng)上關(guān)于Runtime 有許多介紹,有深入有簡單介紹,...
    alanwangmodify閱讀 10,185評論 4 66
  • 向?qū)ο蟀l(fā)送一個無法處理的消息將會導(dǎo)致錯誤,然而,在報告錯誤之前,運行時系統(tǒng)會提供給消息的接收者第二次機(jī)會去處理消息...
    張小明閱讀 1,175評論 0 1

友情鏈接更多精彩內(nèi)容