一柔昼、消息轉(zhuǎn)發(fā)機(jī)制
當(dāng)向某個對象發(fā)送一條消息時垂券,若該對象的方法列表以及它相應(yīng)繼承鏈上的方法列表都無法找到以該消息選擇子作為key的方法實現(xiàn)時侨艾,則會觸發(fā)消息轉(zhuǎn)發(fā)機(jī)制执虹。
1、動態(tài)方法解析
+ (BOOL)resolveInstanceMethod:(SEL)sel;
首先唠梨,當(dāng)接受到未能識別的選擇子時袋励,運行時系統(tǒng)會調(diào)用該函數(shù)用以給對象一次機(jī)會來添加相應(yīng)的方法實現(xiàn),如果用戶在該函數(shù)中動態(tài)添加了相應(yīng)方法的實現(xiàn)当叭,則跳轉(zhuǎn)到方法的實現(xiàn)部分茬故,并將該實現(xiàn)存入緩存中,以供下次調(diào)用蚁鳖。
2磺芭、備援接收者
- (id)forwardingTargetForSelector:(SEL)aSelector;
如果運行時在消息轉(zhuǎn)發(fā)的第一步中未找到所調(diào)用方法的實現(xiàn),那么當(dāng)前接收者還有第二次機(jī)會進(jìn)行未知選擇子的處理醉箕。這時運行期系統(tǒng)會調(diào)用上述方法钾腺,并將未知選擇子作為參數(shù)傳入徙垫,該方法可以返回一個能處理該選擇子的對象,運行時系統(tǒng)會根據(jù)返回的對象進(jìn)行查找放棒,若找到則跳轉(zhuǎn)到相應(yīng)方法的實現(xiàn)姻报,則消息轉(zhuǎn)發(fā)結(jié)束。
3间螟、完整的消息轉(zhuǎn)發(fā)
- (void)forwardInvocation:(NSInvocation *)anInvocation;
當(dāng)運行時系統(tǒng)檢測到第二步中用戶未返回能處理相應(yīng)選擇子的對象時吴旋,那么來到這一步就要啟動完整的消息轉(zhuǎn)發(fā)機(jī)制了。該方法可以改變消息調(diào)用目標(biāo)厢破,運行時系統(tǒng)根據(jù)所改變的調(diào)用目標(biāo)荣瑟,向調(diào)用目標(biāo)方法列表中查詢對應(yīng)方法的實現(xiàn)并實現(xiàn)跳轉(zhuǎn),這種方式和第二步的操作非常相似溉奕。當(dāng)然你也可以修改方法的選擇子褂傀,亦或者向所調(diào)用方法中追加一個參數(shù)等來跳轉(zhuǎn)到相關(guān)方法的實現(xiàn)。
最后加勤,如果消息轉(zhuǎn)發(fā)的第三步還未能處理該未知選擇子的話仙辟,那么最終會調(diào)用NSObject類的如下方法用以異常的拋出,表明該選擇子最終未能處理鳄梅。
- (void)doesNotRecognizeSelector:(SEL)aSelector;
下面附上完整的消息轉(zhuǎn)發(fā)流程圖:
二叠国、消息轉(zhuǎn)發(fā)驗證
好了,看了那么多的理論知識戴尸,相比大家也已經(jīng)累了粟焊,那我們用一個實例來具體說明Runtime的消息轉(zhuǎn)發(fā)機(jī)制吧。我是傳送門~~~
首先新建一個工程孙蒙,并在工程中添加Cat项棠、Dog and Chicken三個類,并在每個類的.h文件中聲明jump方法挎峦,在Cat.m文件中聲明消息轉(zhuǎn)發(fā)的第一步方法:resolveInstanceMethod: 香追,在該方法中動態(tài)添加jump方法的實現(xiàn)。
消息轉(zhuǎn)發(fā)第一步
注:在第一步中動態(tài)添加方法實現(xiàn)用到了Runtime中的class_addMethod方法坦胶,該方法用以向該類的實例對象中添加相應(yīng)的方法實現(xiàn)透典。
然后在main.m文件中調(diào)用Cat實例的jump方法,就會看到在控制臺打印出如下結(jié)果:
消息轉(zhuǎn)發(fā)第一步打印結(jié)果
然后在Dog.m文件中驗證消息轉(zhuǎn)發(fā)第二步過程顿苇,為了讓運行時系統(tǒng)能夠運行到forwardingTargetForSelector:方法峭咒,我們先在resolveInstanceMethod:中返回NO,代碼如下:
消息轉(zhuǎn)發(fā)第二步
然后按照之前的樣子纪岁,在main.m文件中讓Dog也jump起來凑队,運行之后打印結(jié)果如下:
消息轉(zhuǎn)發(fā)第二步打印結(jié)果
最后我們來驗證消息轉(zhuǎn)發(fā)第三步驟的過程。
在最后的Chicken.m文件中我們讓前兩步的方法分別返回NO和nil值幔翰,用以快速觸發(fā)消息轉(zhuǎn)發(fā)機(jī)制中的完整消息轉(zhuǎn)發(fā)機(jī)制顽决。在驗證這一步中我們注意到短条,在調(diào)用forwardInvocation:方法之前我們需要實現(xiàn)methodSignatureForSelector:方法导匣,并將相應(yīng)選擇子的描述返回才菠。
消息轉(zhuǎn)發(fā)第三步
這里我用了改變調(diào)用目標(biāo)這種方式進(jìn)行消息轉(zhuǎn)發(fā)機(jī)制,至于改換選擇子贡定,讀者可以自行嘗試運行哈赋访,具體我已在項目代碼中寫明,最終調(diào)用Chicken實例的jump方法缓待,其打印結(jié)果如下:
消息轉(zhuǎn)發(fā)第三步打印結(jié)果
明明分別調(diào)用了三個動物的jump蚓耽,最后在控制臺只看到了Cat一直在jump。旋炒。步悠。
如果有覺得上述我講的不對的地方歡迎指出,大家多多交流溝通瘫镇。