當(dāng)lookupImpOrForward函數(shù)從cache和methodTable中找不到對(duì)應(yīng)Method楚里,繼續(xù)向下執(zhí)行就會(huì)來(lái)到resolveMethod_locked函數(shù)也就是我們常說(shuō)的動(dòng)態(tài)方法決議
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
resolveMethod_locked
runtimeLock.assertLocked();
ASSERT(cls->isRealized());
runtimeLock.unlock();
if (! cls->isMetaClass()) {
// try [cls resolveInstanceMethod:sel]
resolveInstanceMethod(inst, sel, cls);
}
else {
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
resolveClassMethod(inst, sel, cls);
if (!lookUpImpOrNilTryCache(inst, sel, cls)) {
resolveInstanceMethod(inst, sel, cls);
}
}
// chances are that calling the resolver have populated the cache
// so attempt using it
return lookUpImpOrForwardTryCache(inst, sel, cls, behavior);
只執(zhí)行一次
behavior & LOOKUP_RESOLVER
behavior ^= LOOKUP_RESOLVER;
這倆步操作保證resolveMethod_locked只被執(zhí)行一次
resolveInstanceMethod
static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{
runtimeLock.assertUnlocked();
ASSERT(cls->isRealized());
SEL resolve_sel = @selector(resolveInstanceMethod:);
if (!lookUpImpOrNilTryCache(cls, resolve_sel, cls->ISA(/*authenticated*/true))) {
// Resolver not implemented.
//根類NSObject有默認(rèn)實(shí)現(xiàn)兜底叔磷,不會(huì)走到這里
return;
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
//向cls對(duì)象發(fā)送resolveInstanceMethod:消息外傅,參數(shù)為當(dāng)前的sel
bool resolved = msg(cls, resolve_sel, sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveInstanceMethod adds to self a.k.a. cls
//從方法緩存中再快速查找一遍
IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
if (resolved && PrintResolving) {
if (imp) {
_objc_inform("RESOLVE: method %c[%s %s] "
"dynamically resolved to %p",
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel), imp);
}
else {
// Method resolver didn't add anything?
_objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
", but no new implementation of %c[%s %s] was found",
cls->nameForLogging(), sel_getName(sel),
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel));
}
}
}
resolveClassMethod
static void resolveClassMethod(id inst, SEL sel, Class cls)
{
runtimeLock.assertUnlocked();
ASSERT(cls->isRealized());
ASSERT(cls->isMetaClass());
if (!lookUpImpOrNilTryCache(inst, @selector(resolveClassMethod:), cls)) {
// Resolver not implemented.
// NSObject有兜底實(shí)現(xiàn)
return;
}
Class nonmeta;
{
mutex_locker_t lock(runtimeLock);
nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
// +initialize path should have realized nonmeta already
if (!nonmeta->isRealized()) {
_objc_fatal("nonmeta class %s (%p) unexpectedly not realized",
nonmeta->nameForLogging(), nonmeta);
}
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveClassMethod adds to self->ISA() a.k.a. cls
IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
if (resolved && PrintResolving) {
if (imp) {
_objc_inform("RESOLVE: method %c[%s %s] "
"dynamically resolved to %p",
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel), imp);
}
else {
// Method resolver didn't add anything?
_objc_inform("RESOLVE: +[%s resolveClassMethod:%s] returned YES"
", but no new implementation of %c[%s %s] was found",
cls->nameForLogging(), sel_getName(sel),
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel));
}
}
}
resolveMethod_locked會(huì)發(fā)送resolveInstanceMethod:和resolveClassMethod:消息斤儿,為了減少程序的崩潰提用戶體驗(yàn)吱瘩,蘋果在這里給開發(fā)者一次機(jī)會(huì)去補(bǔ)救壁熄,這個(gè)過(guò)程就叫做動(dòng)態(tài)方法決議敲董。這里也體現(xiàn)了aop編程思想,在objc_msg流程中給開發(fā)者提供了一個(gè)切面空另,切入自己想要處理盆耽,比如安全處理,日志收集等等扼菠。
resolveClassMethod
static void resolveClassMethod(id inst, SEL sel, Class cls)
{
runtimeLock.assertUnlocked();
ASSERT(cls->isRealized());
ASSERT(cls->isMetaClass());
if (!lookUpImpOrNilTryCache(inst, @selector(resolveClassMethod:), cls)) {
// Resolver not implemented.
return;
}
Class nonmeta;
{
mutex_locker_t lock(runtimeLock);
nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
// +initialize path should have realized nonmeta already
if (!nonmeta->isRealized()) {
_objc_fatal("nonmeta class %s (%p) unexpectedly not realized",
nonmeta->nameForLogging(), nonmeta);
}
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveClassMethod adds to self->ISA() a.k.a. cls
IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
if (resolved && PrintResolving) {
if (imp) {
_objc_inform("RESOLVE: method %c[%s %s] "
"dynamically resolved to %p",
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel), imp);
}
else {
// Method resolver didn't add anything?
_objc_inform("RESOLVE: +[%s resolveClassMethod:%s] returned YES"
", but no new implementation of %c[%s %s] was found",
cls->nameForLogging(), sel_getName(sel),
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel));
}
}
}
看完源碼思考倆個(gè)問(wèn)題
1.為什么在resloveInstanceMethod函數(shù)中調(diào)用了一次lookUpImpOrNilTryCache摄杂,resolveMethod_locked函數(shù)最后又調(diào)用了一次lookUpImpOrNilTryCache?這倆次分別有什么作用循榆?
-
第一次TryCache流程分析
本次TryCache析恢,會(huì)調(diào)用lookUpImpOrForWard函數(shù)查找MethodTable。入?yún)ehavior值為4秧饮,找不到imp的話不會(huì)再走動(dòng)態(tài)決議和消息轉(zhuǎn)發(fā)映挂,直接return nil,分支如下:
if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
return nil;
}
所以這次tryCache實(shí)際的作用就是在動(dòng)態(tài)決議添加方法之后浦楣,找到方法袖肥,并調(diào)用log_and_fill_cache函數(shù)存進(jìn)緩存(佐證了下面這段注釋)
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveInstanceMethod adds to self a.k.a. cls
-
第二次TryCache流程分析
這次我們直接看注釋吧
// chances are that calling the resolver have populated the cache
// so attempt using it
調(diào)用動(dòng)態(tài)決議可能填充了得緩存,并嘗試使用它振劳。嗯椎组,第二次tryCache的作用已經(jīng)簡(jiǎn)單明了。
本次調(diào)用入?yún)ehavior值為1历恐,methodTable查找不到imp不會(huì)走動(dòng)態(tài)決議流程寸癌,但會(huì)調(diào)用消息轉(zhuǎn)發(fā)
-
為什么分為倆次呢专筷,一次不行嗎?
為什么不最后查找方法蒸苇,填充緩存再返回磷蛹,反而要先填充緩存,再嘗試從緩存中查找溪烤,這么做有什么好處呢味咳?
有個(gè)關(guān)于多線程的猜想:
假如線程a發(fā)送消息s進(jìn)入了動(dòng)態(tài)決議流程,此時(shí)線程b也發(fā)送消息s檬嘀,這時(shí)候如果緩存中有已添加的imp響應(yīng)消息s槽驶,是不是就不會(huì)繼續(xù)慢速查找,動(dòng)態(tài)決議等后續(xù)流程鸳兽。這么想,動(dòng)態(tài)決議添加的方法是不是越先添加到緩存越好掂铐。
另外一點(diǎn)我們看到resolveClassMethod之后,也嘗試從緩存中查找,而且找不到又調(diào)用了一遍resolveInstanceMethod揍异。
可已看出蘋果開發(fā)者在設(shè)計(jì)這段流程的思考??可能是:
既然你愿意通過(guò)動(dòng)態(tài)方法決議去添加這個(gè)imp全陨,費(fèi)了這么大功夫,很顯然你想使用該imp衷掷,而且使用的頻率可能不低辱姨。既然如此在resolver方法調(diào)用完畢,我就幫你放進(jìn)緩存吧棍鳖。以后你想用直接從緩存中找炮叶。
2. 為什么類resolver之后會(huì)嘗試調(diào)用instance的resolver碗旅?難道instance的resolver還能解決類方法缺失的問(wèn)題渡处?
關(guān)于這個(gè)問(wèn)題,我們來(lái)看張經(jīng)典的如果我們查找一個(gè)類方法沿著繼承鏈最終會(huì)找到NSObject(rootMetaClass的父類是NSObject)祟辟,這會(huì)導(dǎo)致一個(gè)有意思的問(wèn)題:我們的NSObject對(duì)象方法可以響應(yīng)類方法的sel
看個(gè)實(shí)例
給NSObect添加個(gè)instaceMethod
是不是很驚喜医瘫,其實(shí)我們底層對(duì)classMethod和InstanceMethod根本沒(méi)有區(qū)分,classMethod也是InstanceMethod
* class_getClassMethod. Return the class method for the specified
* class and selector.
**********************************************************************/
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
只不過(guò)旧困,找classMethod是從MetaClass查找InstanceMethod醇份,找InstanceMethod是從class找InstanceMethod。
透過(guò)現(xiàn)象看本質(zhì)吼具,這里就可以解釋僚纷,為什么resolveClass完畢,緩存中找不到imp拗盒,會(huì)再次調(diào)用resolveInstance怖竭。顯然,我們給NSObject添加InstanceMethod可以解決問(wèn)題陡蝇,而且可以在這里我們也可以添加classMethod痊臭。畢竟classMethod也是InstanceMethod哮肚。