iOS底層原理13:消息轉(zhuǎn)發(fā)流程

iOS底層原理12:動態(tài)方法決議中探究了動態(tài)方法決議。在動態(tài)決議之后,通過日志輔助功能認(rèn)識到forwardingTargetForSelectormethodSignatureForSelector方法,也就是消息發(fā)送的最后一個流程消息轉(zhuǎn)發(fā)

準(zhǔn)備工作

  • objc4-818 源碼
  • CF源碼
  • 反匯編工具Hopperida

消息轉(zhuǎn)發(fā)

消息發(fā)送在經(jīng)過動態(tài)方法決議后,仍然沒有查找到正真的方法實現(xiàn),此時進(jìn)入消息轉(zhuǎn)發(fā)流程。轉(zhuǎn)發(fā)流程分兩步快速轉(zhuǎn)發(fā)慢速轉(zhuǎn)發(fā)

快速轉(zhuǎn)發(fā)流程

通過日志輔助發(fā)現(xiàn),在崩潰之前會執(zhí)行forwardingTargetForSelector方法,即消息快速流程

forwardingTargetForSelector方法探究

打開Xcode,通過快捷鍵command + shift + 0打開開發(fā)者文檔,然后搜索forwardingTargetForSelector,結(jié)果如下圖

image
  • 根據(jù)開發(fā)者文檔的描述,forwardingTargetForSelector返回了一個重定向?qū)ο?/code>,這個對象來響應(yīng)未實現(xiàn)的方法。

代碼驗證

  • 新建一個iOS工程,創(chuàng)建兩個類HTPersonHTCommon
    • HTPerson類 只有實例方法sayHello、類方法sayBye的聲明,無實現(xiàn)
    • HTCommon類 實現(xiàn)了這兩個方法
image
  • HTPerson中添加forwardingTargetForSelector方法,代碼如下
- (id)forwardingTargetForSelector:(SEL)aSelector {
    NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));
    
    if (aSelector == @selector(sayHello)) {
        return [[HTCommon alloc] init];
    } else if (aSelector == @selector(sayBye)) {
        return [HTCommon class];
    }
    return [NSObject alloc];
}
  • 運行程序,對象方法sayHello已經(jīng)成功調(diào)用了,但是類方法依然會導(dǎo)致崩潰
image

【問題】如何通過消息轉(zhuǎn)發(fā)快速流程,來處理類方法呢?這里猜測需要通過+ (id)forwardingTargetForSelector:(SEL)aSelector {} 來處理類方法

  • 繼續(xù)修改forwardingTargetForSelector方法,代碼如下
- (id)forwardingTargetForSelector:(SEL)aSelector {
    NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));
    
    if (aSelector == @selector(sayHello)) {
        return [[HTCommon alloc] init];
    }
    return [NSObject alloc];
}

+ (id)forwardingTargetForSelector:(SEL)aSelector {
    NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));
    
    if (aSelector == @selector(sayBye)) {
        return [HTCommon class];
    }
    return [NSObject class];
}
image

慢速轉(zhuǎn)發(fā)流程

如果通過快速轉(zhuǎn)發(fā)流程forwardingTargetForSelector還是找不到方法實現(xiàn),接下來蘋果還給了我們一次機(jī)會,即慢速轉(zhuǎn)發(fā)流程

  • 慢速轉(zhuǎn)發(fā)流程methodSignatureForSelector,查看文檔如下:
image
  • methodSignatureForSelector方法返回的是NSMethodSignature對象,該對象包含由給定選擇器標(biāo)識的方法的描述。methodSignatureForSelector一般和forwardInvocation搭配使用,如果methodSignatureForSelector方法返回的是一個nil就不會調(diào)用forwardInvocation

代碼驗證

#pragma mark- 處理對象方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    
    NSLog(@"%s -- %@", __func__, NSStringFromSelector(aSelector));
    if (aSelector == @selector(sayHello)) {
        return [NSMethodSignature signatureWithObjCTypes:"v:@"];
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    
    NSLog(@"%s -- %@", __func__, NSStringFromSelector(anInvocation.selector));
    
    if (anInvocation.selector == @selector(sayHello)) {
        HTCommon *common = [[HTCommon alloc] init];
        anInvocation.target = common;
        return [anInvocation invoke];
    }
    return [super forwardInvocation:anInvocation];
}

#pragma mark- 處理類方法
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    
    NSLog(@"%s -- %@", __func__, NSStringFromSelector(aSelector));
    if (aSelector == @selector(sayBye)) {
        return [NSMethodSignature signatureWithObjCTypes:"v:@"];
    }
    return [super methodSignatureForSelector:aSelector];
}

+ (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"%s -- %@", __func__, NSStringFromSelector(anInvocation.selector));
}

如果methodSignatureForSelector的返回值是NSMethodSignature對象,則會調(diào)用forwardInvocation方法對anInvocation事務(wù)進(jìn)行處理,如果不處理也不會報錯

消息轉(zhuǎn)發(fā)總結(jié)

消息轉(zhuǎn)發(fā)的處理主要分為兩部分:

  • 快速轉(zhuǎn)發(fā)】當(dāng)慢速查找,以及動態(tài)方法決議均沒有找到實現(xiàn)時,進(jìn)行消息轉(zhuǎn)發(fā),首先是進(jìn)行快速消息轉(zhuǎn)發(fā),即走到forwardingTargetForSelector方法
    • 如果返回消息接收者,在消息接收者中還是沒有找到,則進(jìn)入另一個方法的查找流程
    • 如果返回nil,則進(jìn)入慢速消息轉(zhuǎn)發(fā)
  • 慢速轉(zhuǎn)發(fā)】執(zhí)行到methodSignatureForSelector方法
    • 如果返回的方法簽名為nil,則直接崩潰報錯
    • 如果返回的方法簽名不為nil,走到forwardInvocation方法中,對anInvocation事務(wù)進(jìn)行處理,如果不處理也不會報錯

方法調(diào)用流程

image

總結(jié)

至此,objc_msgSend發(fā)送消息的流程就分析完成了,我們可以得出整個方法調(diào)用的流程:

  • 快速查找流程】:在類的緩存cache中查找指定方法的實現(xiàn)
  • 慢速查找流程】:如果緩存中沒有找到,則在類的方法列表中查找(二分查找),如果還是沒找到,則去父類鏈的緩存和方法列表中查找
  • 動態(tài)方法決議】:如果慢速查找還是沒有找到時,第一次補救機(jī)會就是嘗試一次動態(tài)方法決議,即重寫resolveInstanceMethod/resolveClassMethod 方法
  • 消息轉(zhuǎn)發(fā)】:如果動態(tài)方法決議還是沒有找到,則進(jìn)行消息轉(zhuǎn)發(fā),消息轉(zhuǎn)發(fā)中有兩次補救機(jī)會:快速轉(zhuǎn)發(fā)+慢速轉(zhuǎn)發(fā)
  • 如果轉(zhuǎn)發(fā)之后也沒有,則程序直接報錯崩潰unrecognized selector sent to instance

緩存cache快速查找流程 --> 慢速查找流程 --> 動態(tài)決議方法resolveInstanceMethod --> 快速轉(zhuǎn)發(fā)流程forwardingTargetForSelector --> 慢速轉(zhuǎn)發(fā)流程(methodSignatureForSelector) --> resolveInstanceMethod --> forwardInvocation --> 崩潰報錯

補充

hopper反匯編CoreFoundation系統(tǒng)庫

查看崩潰時的堆棧信息,調(diào)用了CoreFoundation系統(tǒng)庫的forwarding_prep_0___forwarding___方法,如下圖

image

下載CoreFoundation源碼,并沒有找到這兩個方法的實現(xiàn),說明這塊內(nèi)容蘋果并沒有對外提供,只是開源了部分CoreFoundation源碼

  • 通過image list獲取所有的鏡像文件列表,找到CoreFoundation庫的文件路徑
image
  • 通過objdump --macho --syms CoreFoundation | grep "forwarding" 查看 CoreFoundation庫的符號表,發(fā)現(xiàn)___forwarding_prep_0_______forwarding___都是本地符號
image

forwarding_prep_0方法

全局搜索__forwarding_prep_0___,發(fā)現(xiàn)只有一個,且會調(diào)用__forwarding__

image

____forwarding___方法

  • 快速轉(zhuǎn)發(fā)流程
    • 如果forwardingTargetForSelector方法沒有實現(xiàn),跳轉(zhuǎn) loc_115baf流程
    • 如果forwardingTargetForSelector方法的返回值是nil,跳轉(zhuǎn) loc_115baf流程
image
  • 慢速轉(zhuǎn)發(fā)流程

    • 如果methodSignatureForSelector沒有實現(xiàn)直接跳轉(zhuǎn)到loc_115f4a流程,最終會進(jìn)入loc_115fc5流程
    • 如果methodSignatureForSelector返回值等于nil跳轉(zhuǎn)到loc_115fc5流程
    • 如果methodSignatureForSelector返回了簽名信息的對象,則會調(diào)用_forwardStackInvocation:方法,最后會執(zhí)行forwardInvocation方法
      image
  • 慢速流程如果沒有實現(xiàn)的話,則會進(jìn)入doesNotRecognizeSelector:方法

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

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

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