正常情況下,要想讓對象能理解某條消息,那么我們必須以程序碼實現(xiàn)出對應(yīng)的方法才行。但是,在編譯期間,向?qū)ο蟀l(fā)送了一個未實現(xiàn)的消息,這并不會報錯,因為在運行時可以繼續(xù)添加方法。
如果向?qū)ο髠鬟f了一個未知的消息,而對象沒法在自己所屬的類中、父類......乃至根類中,找到對應(yīng)的消息實現(xiàn)方法,就會報錯閃退。身為程序員,crash是決定不能接受的啊。遇到這種問題,除了坐等crash,難道就沒有別的法子了?這就是我們今天要了解的內(nèi)容。
其實,在對象沒法處理未知消息、程序crash之前,我們還有三個補救的機會,這就是傳說中的消息轉(zhuǎn)發(fā)機制。
消息轉(zhuǎn)發(fā),分四個步驟。
步驟一:動態(tài)方法解析。+(BOOL)resolveInstanceMethod:(SEL)name;
先征詢對象所屬的類,看其是否能動態(tài)添加方法,以處理當(dāng)前這個未知的消息。如果能,那么消息轉(zhuǎn)發(fā)結(jié)束。如果不能,進入步驟二。
步驟二:備援接收者。- (id)forwardingTargetForSelector:(SEL)aSelector;
再次征詢對象所屬的類,看其是否能找到別的對象來處理這個消息,說白了,就是打算找個有能力的人,把這個燙手山芋扔給那個人來處理。同樣的道理,如果能找到接盤俠,那么消息轉(zhuǎn)發(fā)結(jié)束。如果不能,那么進入步驟三。
步驟三,消息重定向。- (void)forwardInvocation: (NSInvocation*)invocation;
又稱為完整的消息轉(zhuǎn)發(fā)機制。到了這里,runtime 就會把與消息有關(guān)的全部細節(jié),都分裝到NSInvocation 對象中,再給消息接受者最后一個機會,讓其設(shè)法解決當(dāng)前還未處理的這條消息。即對象調(diào)用 forwardInvocation: 方法,如果不能處理就會調(diào)用父類的相關(guān)方法,一直到NSObject的這個方法,如果NSObject都無法處理就會調(diào)用doesNotRecognizeSelector: 方法拋出異常,程序crash。
有個流程圖,解釋的很清楚:

最后,關(guān)于步驟二,其實可以用來模擬“多重繼承”。比如說,有一個a對象,它的內(nèi)部還有其他一系列其他的對象。那么a對象可以利用步驟二,讓其他內(nèi)部對象去處理某個消息,而在外部看來,以為是a對象本身去處理的,感覺跟類簇差不多?
但有個點需要注意,步驟二中,我們是沒法去修改這個消息的內(nèi)容的。
最后的最后,步驟三,可以修改消息的內(nèi)容。運行時在調(diào)用- (void)forwardInvocation:(NSInvocation *)anInvocation之前,會先調(diào)用- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector方法來獲取這個選擇子的方法簽名,然后在 -(void)forwardInvocation:(NSInvocation *)anInvocation 方法中我們就可以通過anInvocation拿到相應(yīng)信息做處理。具體的就不展開了,自行谷歌。