OC中的消息轉(zhuǎn)發(fā)

前言

????????我們知道OC是一門動(dòng)態(tài)語(yǔ)言、它提供了一個(gè)RunTime庫(kù)把代碼中的類型檢測(cè)、方法調(diào)用等一系列操作放到了運(yùn)行期。這固然對(duì)語(yǔ)言的靈活性來說是一極大的優(yōu)勢(shì),但這也給我們開發(fā)帶來了一個(gè)讓人頭疼的問題 ?--> ?《unrecognized selector sent to instance 0x1c400f420》;相信我們?cè)陂_發(fā)中都遇到過這種的Crash提示。

????????下面列舉幾個(gè)導(dǎo)致這種Crash Log的示例代碼。

示例1、

P-1

示例2、

P-2
P-3
P-4

????????正如我們開篇所敘述的那樣在編譯和鏈接期、上面兩個(gè)代碼沒有任何問題、但是在運(yùn)行期就會(huì)出現(xiàn)?*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[ACTestForwardObject length]: unrecognized selector sent to instance 0x1c0202fc0'這樣的Crash Log。

????????那么我們?nèi)绾伪苊馍厦娴倪@些問題呢?在Crash之前OC到底做了什么?我們是否可以在Crash之前對(duì)這個(gè)錯(cuò)誤進(jìn)行補(bǔ)救呢?

????????答案是肯定的、OC在?unrecognized?selector 之前其實(shí)已經(jīng)為我們做了很多次努力(具體來說是三次)來讓我們有足夠的機(jī)會(huì)來挽回APP出現(xiàn)Crash,而這就是我們要說的消息轉(zhuǎn)發(fā)機(jī)制。

一、什么是消息轉(zhuǎn)發(fā)(Message Forward)

????????我們知道OC對(duì)象調(diào)用方法其實(shí)是調(diào)用底層的objc_msgSend()函數(shù),而方法查找的大致過程是通過遍歷當(dāng)前類->父類->根類的方法列表來查找是否有對(duì)應(yīng)的方法(這里對(duì)方法調(diào)用不做過多敘述)。如果在查找過程中找到對(duì)應(yīng)的方法實(shí)現(xiàn),則進(jìn)行方法調(diào)用;如果直到根類依然沒有找到對(duì)應(yīng)的方法實(shí)現(xiàn)、那么接下來便是消息轉(zhuǎn)發(fā) --> Show Time!!!!

????????如果直到根類都沒有遇到傳說中的方法實(shí)現(xiàn),那么OC將觸發(fā)消息轉(zhuǎn)發(fā)機(jī)制。而所謂的消息轉(zhuǎn)發(fā)大概過程如下圖:

P-1-1(消息轉(zhuǎn)發(fā)過程)

? ? ? ? 從圖中我們可以看到如果在繼承關(guān)系的方法列表中沒有找到Method,將進(jìn)行下面的三步去處理不能識(shí)別的方法。

<1>、通過resolveInstanceMethod:我們可以動(dòng)態(tài)的為類添加方法實(shí)現(xiàn)。

<2>、通過forwardingTargetForSelector:我們可以返回一個(gè)可以處理aSelector的對(duì)象。

<3>、通過methodSignatureForSelector:(方法簽名)和forwardInvocation:(封裝方法到Invocation)做最后的掙扎。

如果上面三步對(duì)aSelector做了處理,則程序正常執(zhí)行。否者程序最后將調(diào)用doesNotRecognizeSelector:方法輸出前言中的Crash Log。

二、消息轉(zhuǎn)發(fā)代碼實(shí)現(xiàn)

????????首先我們實(shí)現(xiàn)兩個(gè)類:ACTestForwardObjectACTestInvicationObject。

1.ACTestInvicationObject實(shí)現(xiàn)????

????????ACTestInvicationObject主要作為上面消息轉(zhuǎn)發(fā)中的Target返回,所以該類只實(shí)現(xiàn)一個(gè)方法如下圖:

P-2-1

2.?ACTestForwardObject實(shí)現(xiàn)

ACTestForwardObject.h中我們聲明一個(gè)logClassMethod函數(shù)用于輸出對(duì)象的方法列表內(nèi)容、如下圖。

P-2-2

依次在ACTestForwardObject.m中重寫或?qū)崿F(xiàn)消息轉(zhuǎn)發(fā)相關(guān)的函數(shù)、如下圖:

消息轉(zhuǎn)發(fā)的第一步:

P-2-3

消息轉(zhuǎn)發(fā)的第二步:

P-2-4

消息轉(zhuǎn)發(fā)的第三步:

該函數(shù)主要返回方法簽名,如果返回Nil,forwardInvocation將不會(huì)調(diào)用、直接調(diào)用doesNotRecognizeSelector 導(dǎo)致Crash。

P-2-5

這里對(duì)anInvocation對(duì)象做處理,最后invoke Invocation。

P-2-6

重寫doesNotRecognizeSelector進(jìn)行Log輸出。

P-2-7

其他私有函數(shù)實(shí)現(xiàn):

第三步Invocation被替換的方法實(shí)現(xiàn):

P-2-8

第一步動(dòng)態(tài)添加的方法實(shí)現(xiàn):

P-2-9

用于輸出方法列表的函數(shù):

P-2-10

三、程序調(diào)試驗(yàn)證

我們?cè)诔绦騿?dòng)的時(shí)候調(diào)用如下代碼:

P-3-1

驗(yàn)證一:

按照我們截圖的代碼運(yùn)行程序輸出Log如下:(注意這里第二步和第三步中我取了非,實(shí)際沒有進(jìn)入判斷條件)根據(jù)Log我們?cè)倩乜磮DP1-1-1消息轉(zhuǎn)發(fā)過程是不是更清楚了?.

P-3-2

驗(yàn)證二:

? ? ? ? 在驗(yàn)證一條件的基礎(chǔ)上,將P-2-3中的isResolve條件取反,并放開注釋掉的????IMPdefaultMethod =(IMP)acDefaultRecognizedSelector; class_addMethod(self.class, sel, defaultMethod,"v@:");重新運(yùn)行程序輸出Log如下:

P-3-3

驗(yàn)證三:

? ? ? ? 在驗(yàn)證一條件的基礎(chǔ)上,將圖P-2-4中的[targetObjectrespondsToSelector:@selector(length)]前面的取非去掉,重新運(yùn)行程序,輸出Log如下:

P-3-4

驗(yàn)證四:

? ? 在驗(yàn)證一條件的基礎(chǔ)上,將圖P-2-6中的isEqual前面的取反去掉,重新運(yùn)行程序,輸出Log如下:

P-3-5

驗(yàn)證五:

在驗(yàn)證一條件的基礎(chǔ)上,我們將圖P-2-5中的[method isEqualToString:@"length"]取反;并將圖P-2-6中的isEqual前面的取反去掉,重新運(yùn)行程序。輸出Log如下:

P-3-5

四、總結(jié)

? ??????????通過上面的1、2、3、4驗(yàn)證及Log輸出,我們可以相信OC中的消息轉(zhuǎn)發(fā)分為三個(gè)步驟:(1)、resolveInstanceMethod,(2)、forwardingTargetForSelector,(3)methodSignatureForSelector +?forwardInvocation;這三個(gè)步驟是按照順序依次調(diào)用的、如果步驟1對(duì)aSelector進(jìn)行了處理、那么之后的2、3將不在執(zhí)行;依次類推如果三步都沒有對(duì)aSelector做處理那么將調(diào)用doesNotRecognizeSelector之后輸出系統(tǒng)Log程序Crash。通過驗(yàn)證5我們可以推斷出第三步中methodSignatureForSelector是必要條件。如果methodSignatureForSelector返回Nil,則forwardInvocation將不會(huì)被調(diào)用。

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

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

  • 在本文中,將為你解釋在OC的動(dòng)態(tài)機(jī)制中,一個(gè)對(duì)象是如何調(diào)用,并且在對(duì)象中找不到方法的情況下,如何將方法通過"發(fā)消息...
    歐陽(yáng)銓閱讀 816評(píng)論 2 5
  • ??當(dāng)我們給一個(gè)對(duì)象發(fā)送了一個(gè)沒有的方法,系統(tǒng)會(huì)拋出異常并打印,[unrecognized selector se...
    阿洋12138閱讀 123評(píng)論 0 0
  • 消息轉(zhuǎn)發(fā) 描述:如果類不能執(zhí)行這個(gè)方法,會(huì)執(zhí)行動(dòng)態(tài)消息轉(zhuǎn)發(fā),如果該類還是不能動(dòng)態(tài)的添加方法,則走完整的消息轉(zhuǎn)發(fā)。分...
    董二千閱讀 415評(píng)論 0 0
  • OC是消息型語(yǔ)言,OC中的方法調(diào)用實(shí)際上是消息的發(fā)送,編譯器并不能決定程序真正執(zhí)行的到底是哪段代碼,這個(gè)工作,需要...
    ghost__閱讀 169評(píng)論 0 0
  • 這幾天悟出一個(gè)理兒,別總覺得自己特別牛,懷才不遇像千里馬沒遇見伯樂。 等你真正成為千里馬,自然會(huì)接觸伯樂。 我可能...
    清茶煮酒minha閱讀 171評(píng)論 0 2

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