iOS底層-動(dòng)態(tài)方法決議與消息轉(zhuǎn)發(fā)

動(dòng)態(tài)方法決議

我們?nèi)炙阉鱨ookUpImpOrForward,最后在objc-runtime-new.mm文件中找到了源碼實(shí)現(xiàn),這是一個(gè)c實(shí)現(xiàn)的函數(shù),源碼如下:


圖1
圖2

我們發(fā)現(xiàn)源碼中有個(gè)resolveMethod_locked,點(diǎn)擊進(jìn)入發(fā)現(xiàn)源碼如下:

static NEVER_INLINE IMP
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
    runtimeLock.assertLocked();
    ASSERT(cls->isRealized());

    runtimeLock.unlock();
    // 動(dòng)態(tài)方法決議 : 給一次機(jī)會(huì) 重新查詢
    if (! cls->isMetaClass()) {  // 對(duì)象 - 類
        // try [cls resolveInstanceMethod:sel]
        resolveInstanceMethod(inst, sel, cls);
    } 
    else { // 類方法 - 元類
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        resolveClassMethod(inst, sel, cls);
        if (!lookUpImpOrNil(inst, sel, cls)) {  // 為什么要有這行代碼
            resolveInstanceMethod(inst, sel, cls);
        }
    }

    // chances are that calling the resolver have populated the cache
    // so attempt using it
    return lookUpImpOrForward(inst, sel, cls, behavior | LOOKUP_CACHE);
}

我們發(fā)現(xiàn)如果是元類的話就走resolveClassMethod,不是元類的話就走resolveInstanceMethod,接下來(lái)代碼分析

代碼分析

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        LGPerson *person = [LGPerson alloc];
        [LGPerson sayNB];

    }
    return 0;
}

如果這個(gè)方法沒(méi)有實(shí)現(xiàn),會(huì)報(bào)錯(cuò)誤如下:


錯(cuò)誤圖

實(shí)現(xiàn)方案

我們?cè)贚GPerson這個(gè)類中實(shí)現(xiàn)如下代碼:

+ (BOOL)resolveClassMethod:(SEL)sel{
    NSLog(@"%@ 來(lái)了",NSStringFromSelector(sel));
    if (sel == @selector(sayNB)) {

        IMP imp           = class_getMethodImplementation(objc_getMetaClass("LGPerson"), @selector(lgClassMethod));
        Method sayMMethod = class_getInstanceMethod(objc_getMetaClass("LGPerson"), @selector(lgClassMethod));
        const char *type  = method_getTypeEncoding(sayMMethod);
        return class_addMethod(objc_getMetaClass("LGPerson"), sel, imp, type);
    }
    return [super resolveClassMethod:sel];
}

其實(shí)我們之前的探索以及isa的走位圖,我們可以發(fā)現(xiàn)類方法存在元類中,實(shí)際上也是元類中的實(shí)例方法。

消息轉(zhuǎn)發(fā)機(jī)制

圖1

我們通過(guò)圖1可以發(fā)現(xiàn)在執(zhí)行doesNotRecognizedSelector之前,執(zhí)行forwarding_prep_0forwarding。
那么接下我們要去尋找forwarding_prep_0
forwarding,我們下載CoreFoundation開(kāi)源了的代碼里面查找,發(fā)現(xiàn)找不到。
接下來(lái)通過(guò)image list指令查看所有的編譯文件
我們找到CoreFoundation的編譯文件路徑:/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation

圖2

接下來(lái)我們通過(guò)hopper軟件對(duì)這個(gè)CoreFoundation編譯文件進(jìn)行反匯編


圖3

圖4

forwarding的偽代碼里面,我們看到:
先找forwardingTargetForSelector,
如果找到forwardingTargetForSelector,消息轉(zhuǎn)發(fā)
如果沒(méi)有找到forwardingTargetForSelector,就會(huì)找methodSignatureForSelector
如果沒(méi)有找到methodSignatureForSelector,直接unrecognized selector
如果找到methodSignatureForSelector,會(huì)繼續(xù)尋找forwardInvocation,
如果沒(méi)有找到forwardInvocation,直接unrecognized selector
如果找到forwardInvocation,消息轉(zhuǎn)發(fā)。

最后編輯于
?著作權(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ù)。

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