Objective C語言把能在運行期做的事情就推遲到運行期再決定。這就意味著,Objective C不僅需要一個編譯器,而且需要一個運行期環(huán)境。這個運行期環(huán)境就是Runtime。而消息機制也是位于<objc/message.h>。
1、如果我們調(diào)用一個對象只有聲明了方法并沒有對其實現(xiàn)的話編譯器在運行時會報錯為
-[animal wight]: unrecognized selector sent to instance 0x1345809a0
2017-11-07 13:49:52.185 method[2076:1036713] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[animal wight]: unrecognized selector sent to instance 0x1345809a0'
*** First throw call stack:
消息轉(zhuǎn)發(fā)機制一般會有三個過程
一、第一過程
+ (BOOL)resolveInstanceMethod:(SEL)sel(對象方法沒有實現(xiàn))
+ (BOOL)resolveClassMethod:(SEL)sel (類方法沒有實現(xiàn))
二、第二過程
- (id)forwardingTargetForSelector:(SEL)aSelector
三 、第三過程
- (NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector
-(void)forwardInvocation:(NSInvocation*)anInvocation
1、如果我們在第一過程中動態(tài)添加了一個方法的實現(xiàn)了過程二和過程三就不會在執(zhí)行、如果在第二過程做了處理 第三過程也不會在執(zhí)行。如果過程一、二、三、都沒有處理那么程序就會拋出異常。
下圖是整個過程

2、如果我們在resolveInstanceMethod:(SEL)sel在這個方法中做了處理會怎么樣呢?可以發(fā)現(xiàn)程序沒有再拋出異常了 而是調(diào)用了我們增加的方法實現(xiàn) 解釋一下這幾個字段的意思可以發(fā)現(xiàn)僅僅調(diào)用了方法一, 二、三、四都沒有調(diào)用。
關(guān)于生成簽名的類型"v@:"解釋一下。每一個方法會默認隱藏兩個參數(shù),self、_cmd,self代表方法調(diào)用者,_cmd代表這個方法的SEL,簽名類型就是用來描述這個方法的返回值、參數(shù)的,v代表返回值為void,@表示self,:表示_cmd。

3、如果我們在方法一中沒有做處理而是在方法二中做了處理其中的 car這個類中我在其的.m中實現(xiàn)了eat這個方法,就會將這個消息拋給car這個類中去尋找實現(xiàn)eat的方法。


4、如果- (id)forwardingTargetForSelector:(SEL)aSelector這個方法沒有做處理
methodSignatureForSelector和forwardInvocationmethodSignatureForSelector用來生成方法簽名,這個簽名就是給forwardInvocation中的參數(shù)NSInvocation調(diào)用的。
開頭我們要找的錯誤unrecognized selector sent to instance原因,原來就是因為methodSignatureForSelector這個方法中,由于沒有找到run對應(yīng)的實現(xiàn)方法,所以返回了一個空的方法簽名,最終導(dǎo)致程序報錯崩潰。
所以我們需要做的是自己新建方法簽名,再在forwardInvocation中用你要轉(zhuǎn)發(fā)的那個對象調(diào)用這個對應(yīng)的簽名,這樣也實現(xiàn)了消息轉(zhuǎn)發(fā)。


其中Delegate中實現(xiàn)了eat這個方法 。