上节我们分析了objc_msgSend的快速查找部分(汇编部分),这一节我们来分析其慢速部分,也就是c语言部分。
接下来我们通过在代码中打断点来跟进一下这个流程。
我们将断点的显示模式改为汇编显示。
然后将代码运行起来。
我们可以很清楚的看到,对象在调用方法时,底层是通过调用objc_msgSend函数来发送消息的,我们在objc_msgSend函数处打上断点,继续跟进去看看。
我们可以清楚的看到,objc_msgSend在缓存没有命中之后调用了_objc_msgSend_uncached。
_objc_msgSend_uncached
_objc_msgSend_uncached调用了_class_lookupMethodAndLoadCache3
_class_lookupMethodAndLoadCache3
通过上面的图片,我们可以清楚的看到_class_lookupMethodAndLoadCache3函数是存在于objc-runtime-new.mm:4845这个文件中。接下来我们来看看这个函数中都做了些什么。
IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
{
return lookUpImpOrForward(cls, sel, obj,
YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
}
lookUpImpOrForward
在类对象的方法列表中查找IMP。
{
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
imp = meth->imp;
goto done;
}
}
如果没找到,继续在父类的缓存的方法列表中查找IMP。
{
unsigned attempts = unreasonableClassCount();
for (Class curClass = cls->superclass;
curClass != nil;
curClass = curClass->superclass)
{
// Halt if there is a cycle in the superclass chain.
if (--attempts == 0) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.
imp = cache_getImp(curClass, sel);
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
// Found the method in a superclass. Cache it in this class.
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;
}
else {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
}
// Superclass method list.
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
goto done;
}
}
}
如果IMP没有找到,则尝试做一次动态方法解析。
if (resolver && !triedResolver) {
runtimeLock.unlock();
_class_resolveMethod(cls, sel, inst);//这里做一次动态方法解析。
runtimeLock.lock();
// Don't cache the result; we don't hold the lock so it may have
// changed already. Re-do the search from scratch instead.
triedResolver = YES;
goto retry;
}
如果没有找到IMP,并且方法解析也没有处理,那么则进入消息转发流程。
// No implementation found, and method resolver didn't help.
// Use forwarding.
imp = (IMP)_objc_msgForward_impcache;
总结
1.当在 objc_msgSend 缓存中没有找到方法,就会来到 class_lookupMethodAndLoadCache3 进行慢速查找流程。
2.在 lookUpImpOrForward 里面会先去本类当中查找方法 getMethodNoSuper_nolock,本类没有找到就会去递归的去父类当中查找。
3.如果本类和父类都没有找到,就会进行动态方法决议 _class_resolveMethod,这是苹果爸爸给我们的最后一次机会。
4.动态方法我们还不处理,最后就会走到 _objc_forward_handler,然后崩溃报错 selector sent to instance。