Runtime底層原理總結--反彙編分析消息轉發

消息轉發:發送一個消息,也就是sel查找imp,當沒有找到imp,接下來進入動態方法解析,如果開發者並沒有處理,會進入消息轉發。

消息轉發

前幾篇文章介紹了Runtime底層原理動態方法解析總結

,我們知道如果前面的動態方法解析也沒有解決問題的話,那麼就會進入消息轉發_objc_msgForward_impcache方法,會有快速消息轉發和慢速消息轉發。
_objc_msgForward_impcache方法會從C轉換到彙編部分__objc_msgForward_impcache進行快速消息轉發,執行閉源__objc_msgForward

如果我們的方法沒有查找到會報錯_forwarding_prep_0

但是我們在源代碼中找不到該方法,除了前面文章--Runtime底層原理--動態方法解析、消息轉發源碼分析提到的方法外,我們可以用反彙編分析消息轉發。

首先進入/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/CoreFoundation.framework中,找到可執行文件CoreFoundation,將該文件拖入hopper中,找到CFInitialze可以看到了___forwarding_prep_0___

進入___forwarding_prep_0___內部,

從崩潰堆棧信息中看到有執行forwarding,進入forwarding內部,裏面判斷了_objc_msgSend_stret、_objc_msgSend、taggedpointer之後有個forwardingTargetForSelector:,判斷forwardingTargetForSelector:沒有實現,沒有實現跳轉到loc_126fc7,

進入loc_126fc7,判斷殭屍對象之後執行方法簽名methodSignatureForSelector:,如果有值,獲取Name、是否有效的位移等,之後會響應_forwardStackInvocation:

如果沒有響應_forwardStackInvocation:,則會響應forwardInvocation:,給rdi發送rax消息,rdi就是NSInvocation,rax就是selforwardInvocation:這就是消息轉發的流程

_objc_msgForward部分進行反彙編成OC代碼:


int __forwarding__(void *frameStackPointer, int isStret) {
    id receiver = *(id *)frameStackPointer;
    SEL sel = *(SEL *)(frameStackPointer + 8);
    const char *selName = sel_getName(sel);
    Class receiverClass = object_getClass(receiver);
    
    // 調用 forwardingTargetForSelector:
    if (class_respondsToSelector(receiverClass, @selector(forwardingTargetForSelector:))) {
        id forwardingTarget = [receiver forwardingTargetForSelector:sel];
        if (forwardingTarget && forwarding != receiver) {
            if (isStret == 1) {
                int ret;
                objc_msgSend_stret(&ret,forwardingTarget, sel, ...);
                return ret;
            }
            return objc_msgSend(forwardingTarget, sel, ...);
        }
    }
    
    // 殭屍對象
    const char *className = class_getName(receiverClass);
    const char *zombiePrefix = "_NSZombie_";
    size_t prefixLen = strlen(zombiePrefix); // 0xa
    if (strncmp(className, zombiePrefix, prefixLen) == 0) {
        CFLog(kCFLogLevelError,
              @"*** -[%s %s]: message sent to deallocated instance %p",
              className + prefixLen,
              selName,
              receiver);
        <breakpoint-interrupt>
    }
    
    // 調用 methodSignatureForSelector 獲取方法簽名後再調用 forwardInvocation
    if (class_respondsToSelector(receiverClass, @selector(methodSignatureForSelector:))) {
        NSMethodSignature *methodSignature = [receiver methodSignatureForSelector:sel];
        if (methodSignature) {
            BOOL signatureIsStret = [methodSignature _frameDescriptor]->returnArgInfo.flags.isStruct;
            if (signatureIsStret != isStret) {
                CFLog(kCFLogLevelWarning ,
                      @"*** NSForwarding: warning: method signature and compiler disagree on struct-return-edness of '%s'.  Signature thinks it does%s return a struct, and compiler thinks it does%s.",
                      selName,
                      signatureIsStret ? "" : not,
                      isStret ? "" : not);
            }
            if (class_respondsToSelector(receiverClass, @selector(forwardInvocation:))) {
                NSInvocation *invocation = [NSInvocation _invocationWithMethodSignature:methodSignature frame:frameStackPointer];
                
                [receiver forwardInvocation:invocation];
                
                void *returnValue = NULL;
                [invocation getReturnValue:&value];
                return returnValue;
            } else {
                CFLog(kCFLogLevelWarning ,
                      @"*** NSForwarding: warning: object %p of class '%s' does not implement forwardInvocation: -- dropping message",
                      receiver,
                      className);
                return 0;
            }
        }
    }
    
    SEL *registeredSel = sel_getUid(selName);
    
    // selector 是否已經在 Runtime 註冊過
    if (sel != registeredSel) {
        CFLog(kCFLogLevelWarning ,
              @"*** NSForwarding: warning: selector (%p) for message '%s' does not match selector known to Objective C runtime (%p)-- abort",
              sel,
              selName,
              registeredSel);
    } // doesNotRecognizeSelector
    else if (class_respondsToSelector(receiverClass,@selector(doesNotRecognizeSelector:))) {
        [receiver doesNotRecognizeSelector:sel];
    }
    else {
        CFLog(kCFLogLevelWarning ,
              @"*** NSForwarding: warning: object %p of class '%s' does not implement doesNotRecognizeSelector: -- abort",
              receiver,
              className);
    }
    
    // The point of no return.
    kill(getpid(), 9);
}

該文章爲記錄本人的學習路程,希望能夠幫助大家,也歡迎大家點贊留言交流!!!文章地址:https://www.jianshu.com/p/29d0272a97ff

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章