OC中的方法調(diào)用, 其實(shí)都是轉(zhuǎn)換為objc_msgSend
函數(shù)的調(diào)用
objc_msgSend
的執(zhí)行流程可以分為3大階段
- 消息發(fā)送
- 動(dòng)態(tài)方法解析
- 消息轉(zhuǎn)發(fā)
源碼是用匯編寫的, 但可以大致看一下:
方法調(diào)用最后走到了緩存查找里面:
CacheLookup
如果方法在緩存中沒有找到, 就會(huì)調(diào)用: __objc_msgSend_uncached
我們看看
MethodTableLookup
里面發(fā)生了什么:
調(diào)用了_lookUpImpOrForward
, 意思是找到這個(gè)函數(shù)的指針, 然后我們?cè)谶@個(gè)類objc-runtime-new
中找到了它的方法實(shí)現(xiàn):
在這個(gè)方法里, 它先去當(dāng)前類的緩存里找方法, 如果找到, 直接返回
imp = cache_getImp(curClass, sel);
if (imp) goto done_unlock;
如果緩存里沒有, 就去當(dāng)前類的方法列表里找getMethodNoSuper_nolock
找到了就繼續(xù)走到
done
那里:這里會(huì)將找到的方法緩存到當(dāng)前類:
log_and_fill_cache(cls, imp, sel, inst, curClass);
如果找不到, 就會(huì)去父類的緩存里找
如果找到了, 就緩存到當(dāng)前類(不是找到的父類)
如果沒找到, 就繼續(xù)在父類的方法列表里找.
如此循環(huán)往復(fù), 如下圖所示:
經(jīng)過這么一個(gè)過程, 如果找到了, 也就是這個(gè)匯編方法MethodTableLookup
將存在于x0
寄存器中的函數(shù)指針移動(dòng)到x17
寄存器
也就是說, 現(xiàn)在
x17
寄存器里存有了函數(shù)指針, 然后調(diào)用TailCallFunctionPointer
如果經(jīng)過整個(gè)一個(gè)過程都沒有找到方法IMP
, 那么就會(huì)進(jìn)入下一個(gè)階段:
動(dòng)態(tài)方法解析
注釋中是這么說的:
No implementation found. Try method resolver once
找不到方法, 嘗試一次方法解析, 那么resolveMethod_locked
又做了什么呢?
如果這個(gè)類不是元類對(duì)象,動(dòng)態(tài)方法解析就走到這里resolveInstanceMethod
,如果是元類對(duì)象,先調(diào)用resolveClassMethod
比如我在一個(gè)Person
類中實(shí)現(xiàn)resolveInstanceMethod
:
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(run)) {
return YES;
}
return [super resolveInstanceMethod:sel];
}
我開始以為只要實(shí)現(xiàn)了這個(gè)類, 返回YES
,就可以了, 但事實(shí)上還是會(huì)報(bào)經(jīng)典的找不到方法的錯(cuò)誤. 所以還是得真正去解析, 為什么呢?
以為最終還是要調(diào)用lookUpImpOrForwardTryCache
- (void)other {
NSLog(@"%s", __func__);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(run)) {
Method otherMethod = class_getInstanceMethod([Person class], @selector(other));
class_addMethod([Person class], sel, method_getImplementation(otherMethod), method_getTypeEncoding(otherMethod));
return YES;
}
return [super resolveInstanceMethod:sel];
}
例如, 動(dòng)態(tài)添加了一個(gè)方法, 而添加的方法是確實(shí)存在的. 這樣才是一個(gè)真正的動(dòng)態(tài)方法解析. 整個(gè)流程如下: