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


我們發(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ò)誤如下:

實(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ī)制

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

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


在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ā)。