_objc_msgForward的作用,直接調(diào)用會發(fā)生什么

demo地址

_objc_msgForward是IMP類型的,用于消息轉(zhuǎn)發(fā)的,當(dāng)像一個對象發(fā)送消息,但他沒有實(shí)現(xiàn)的時候,_objc_msgForward會嘗試做消息轉(zhuǎn)發(fā)。

objc_msgSend的動作比較清晰,在“消息傳遞”過程中,:首先在 Class 中的緩存查找 IMP (沒緩存則初始化緩存),如果沒找到,則向父類的 Class 查找。如果一直查找到根類仍舊沒有實(shí)現(xiàn),則用_objc_msgForward函數(shù)指針代替 IMP 。最后,執(zhí)行這個 IMP 。

_objc_msgForward消息轉(zhuǎn)發(fā)需要做的幾件事:

1. 調(diào)用+ (BOOL)resolveInstanceMethod:(SEL)sel(或 + (BOOL)resolveClassMethod:(SEL)sel)方法,在此方法中添加相應(yīng)selector以及IMP即可,允許用戶在此時為該Class動態(tài)添加實(shí)現(xiàn)。如果有實(shí)現(xiàn)了,則調(diào)用并返回YES,那么重新開始o(jì)bjc_msgSend流程。對象會相應(yīng)這個選擇器,一般是因?yàn)樗呀?jīng)調(diào)用過class_addMethod。如果仍沒實(shí)現(xiàn),繼續(xù)下面的步驟
2. 調(diào)用- (id)forwardingTargetForSelector:(SEL)aSelector方法,嘗試找到一個能相應(yīng)該消息的對象。如果獲取到,則直接把消息轉(zhuǎn)發(fā)給它,返回非nil對象。否則返回 nil ,繼續(xù)下面的動作。
3. 調(diào)用- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector方法,嘗試獲得一個方法簽名。如果能獲取,則返回非nil:創(chuàng)建一個 NSlnvocation 并傳給forwardInvocation:
調(diào)用- (void)forwardInvocation:(NSInvocation *)anInvocation方法,將獲取到的方法簽名包裝成 Invocation 傳入,如何處理就在這里面了,并返回非nil。如果獲取不到,則直接調(diào)用4拋出異常。
4. 調(diào)用- (void)doesNotRecognizeSelector:(SEL)aSelector,默認(rèn)的實(shí)現(xiàn)是拋出異常。如果第3步?jīng)]能獲得一個方法簽名,執(zhí)行該步驟。

上面前4個方法均是模板方法,開發(fā)者可以override,由 runtime 來調(diào)用。最常見的實(shí)現(xiàn)消息轉(zhuǎn)發(fā):就是重寫步驟3的兩個方法,吞掉一個消息或者代理給其他對象都是沒問題的

也就是說_objc_msgForward在進(jìn)行消息轉(zhuǎn)發(fā)的過程中會涉及以下這幾個方法:

    • (BOOL)resolveInstanceMethod:(SEL)sel方法 (或 + (BOOL)resolveClassMethod:(SEL)sel)。
    • (id)forwardingTargetForSelector:(SEL)aSelector方法
    • (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector方法
    • (void)forwardInvocation:(NSInvocation *)anInvocation方法
    • (void)doesNotRecognizeSelector:(SEL)aSelector 方法

一共有三次機(jī)會。假設(shè)有A類和B類,分別對應(yīng)a對象,b對象,a執(zhí)行了一個不存在的方法

[self performSelector:@selector(sel:) withObject:@"haha"];

第一次機(jī)會,在+ (BOOL)resolveInstanceMethod:(SEL)sel方法中可以通過class_addMethod(self.class, sel, (IMP)dynamicMethodIMP, "@@:"); 添加動態(tài)方法

id dynamicMethodIMP(id self, SEL _cmd, NSString *str)
{
    NSLog(@"%s:動態(tài)添加的方法",__FUNCTION__);
    NSLog(@"%@", str);
    return @"1";
}

添加成功后不會再繼續(xù)執(zhí)行
第二次機(jī)會,- (id)forwardingTargetForSelector:(SEL)aSelector 系統(tǒng)給了個將這個SEL轉(zhuǎn)給其他對象的機(jī)會,返回b對象,b會重新走一次本過程。

注意,這里不要返回 self,會形成死循環(huán)。

第三次機(jī)會,這個函數(shù)和后面的forwardInvocation:是最后一個尋找IML的機(jī)會。這個函數(shù)讓重載方有機(jī)會拋出一個函數(shù)的簽名,再由后面的forwardInvocation:去執(zhí)行。
doesNotRecognizeSelector作為找不到函數(shù)實(shí)現(xiàn)的最后一步,NSObject實(shí)現(xiàn)這個函數(shù)只有一個功能,就是拋出異常。

雖然理論上可以重載這個函數(shù)實(shí)現(xiàn)保證不拋出異常(不調(diào)用super實(shí)現(xiàn)),但是蘋果文檔著重提出“一定不能讓這個函數(shù)就這么結(jié)束掉,必須拋出異?!薄?/p>

總結(jié)一下,在一個函數(shù)找不到時,Objective-C提供了三種方式去補(bǔ)救:

1、調(diào)用resolveInstanceMethod給個機(jī)會讓類添加這個實(shí)現(xiàn)這個函數(shù)

2、調(diào)用forwardingTargetForSelector讓別的對象去執(zhí)行這個函數(shù)

3、調(diào)用methodSignatureForSelector(函數(shù)符號制造器)和forwardInvocation(函數(shù)執(zhí)行器)靈活的將目標(biāo)函數(shù)以其他形式執(zhí)行。

如果都不行,調(diào)用doesNotRecognizeSelector拋出異常。

直接調(diào)用_objc_msgForward很危險,如果用不好會直接導(dǎo)致程序Crash。

_objc_msgForward隸屬 C 語言,有三個參數(shù)
_objc_msgForward是 IMP 類型,用于消息轉(zhuǎn)發(fā)的:當(dāng)向一個對象發(fā)送一條消息,但它并沒有實(shí)現(xiàn)的時候,_objc_msgForward會嘗試做消息轉(zhuǎn)發(fā)。一旦調(diào)用_objc_msgForward,將跳過查找 IMP 的過程,直接觸發(fā)“消息轉(zhuǎn)發(fā)”。

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

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,030評論 0 9
  • 消息發(fā)送和轉(zhuǎn)發(fā)流程可以概括為:消息發(fā)送(Messaging)是 Runtime 通過 selector 快速查找 ...
    lylaut閱讀 1,983評論 2 3
  • 我們常常會聽說 Objective-C 是一門動態(tài)語言,那么這個「動態(tài)」表現(xiàn)在哪呢?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,319評論 0 7
  • 本文詳細(xì)整理了 Cocoa 的 Runtime 系統(tǒng)的知識,它使得 Objective-C 如虎添翼,具備了靈活的...
    lylaut閱讀 864評論 0 4
  • 我曾經(jīng)有多喜歡你我不知道,甚至我都忘了喜歡你是什么感覺了。只是后來每到想哭的時候,每到在被窩里聽到下雨聲的時候,就...
    Ssshark閱讀 327評論 0 0

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