我们从之前的方法的本质objc_msgSend分析一篇中得知了解到objc_msgSend发送消息进入到了汇编,找不到缓存的时候就会来到__objc_msgSend_uncached -- > MethodTableLookup --> __class_lookupMethodAndLoadCache3,现在我们通过断点调试来分析一下。
断点调试分析
在调用方法处打上断点,开启Always Show Disassembly
屏幕快照 2020-03-07 下午10.51.47.png 屏幕快照 2020-03-07 下午10.55.50.png
然后进入objc_msgSend,往下翻,看到下面有个_objc_msgSend_uncached方法
屏幕快照 2020-03-07 下午10.57.15.png
在这里加断点,继续往下走,找到方法_class_lookupMethodAndLoadCache3
屏幕快照 2020-03-07 下午10.59.37.png
在源码中全局搜索_class_lookupMethodAndLoadCache3方法,找到方法实现
extern IMP _class_lookupMethodAndLoadCache3(id, SEL, Class);
点进去
IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
{
return lookUpImpOrForward(cls, sel, obj,
YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
}
到此,方法查找成功从汇编过渡到C++,这就是真正的方法查找实现。
代码分析方法查找流程
对象方法测试
对象的实例方法 - 自己有 - 返回自己
对象的实例方法 - 自己没有 - 父类有 - 返回父类的
对象的实例方法 - 自己没有 - 父类也没有 - 找父类的父类 - NSObject有 - 返回NSObject的
对象的实例方法 - 自己没有 - 父类也没有 - 找父类的父类 - NSObject也没有 - 崩溃
类方法测试
类方法 - 自己有 - 返回自己
类方法 - 自己没有 - 父类有 - 返回父类的
类方法 - 自己没有 - 父类也没有 - 找父类的父类 - NSObject有 - 返回NSObject的
类方法 - 自己没有 - 父类也没有 - 找父类的父类 - NSObject也没有 - 崩溃
类方法 - 自己没有 - 父类也没有 - 找父类的父类 - NSObject也没有 - 但是有对象方法 - 返回NSObject的实例方法
这里通过一个经典的isa走位图来解释说明
isa流程图.png
源码分析方法查找流程
这里我们直接定位到_class_lookupMethodAndLoadCache3这个方法
IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
{
return lookUpImpOrForward(cls, sel, obj,
YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
}
然后进入lookUpImpOrForward这个方法,这里cache传的值为NO,证明缓存中没有找到,需要进行方法查找,也就是慢速方法查找流程
然后开始分析lookUpImpOrForward这个方法
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = nil;
bool triedResolver = NO;
runtimeLock.assertUnlocked();
// Optimistic cache lookup 这里cache传的NO,所以不走这里的逻辑
if (cache) {
imp = cache_getImp(cls, sel);
if (imp) return imp;
}
// runtimeLock is held during isRealized and isInitialized checking
// to prevent races against concurrent realization.
// runtimeLock is held during method search to make
// method-lookup + cache-fill atomic with respect to method addition.
// Otherwise, a category could be added but ignored indefinitely because
// the cache was re-filled with the old value after the cache flush on
// behalf of the category.
runtimeLock.lock(); //这里加了锁,是为了防止同时查找两个方法时返回imp错误
checkIsKnownClass(cls);
//从类里查找方法,首先要处理好类,做准备条件,判断类有没有加载好,如果没有加载好,那就先加载一下类信息,准备好父类、元类
if (!cls->isRealized()) {
realizeClass(cls);
}
if (initialize && !cls->isInitialized()) {
runtimeLock.unlock();
_class_initialize (_class_getNonMetaClass(cls, inst));
runtimeLock.lock();
// If sel == initialize, _class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen. 2778172
}
retry:
runtimeLock.assertLocked();
// Try this class's cache.
imp = cache_getImp(cls, sel);
if (imp) goto done;
//这里大括号意思是为了形成局部域
// 从当前类上查找,这里是从类的方法列表中查找 IMP。通过 getMethodNoSuper_nolock 查找 Method,找到了之后就调用 log_and_fill_cache 进行缓存的填充,然后返回 imp。
// Try this class's method lists.
{
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
imp = meth->imp;
goto done;
}
}
// Try superclass caches and method lists.
//在自己类中找不到的话再到父类方法列表中找
{
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.从缓存中查找,找到就返回img找不到就break
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;
}
}
}
// No implementation found. Try method resolver once.
//如果方法仍然没找到,首先会进行动态方法解析_class_resolveMethod,在这个过程中,系统会调用一次已经存在的事先定义好的两个类方法,在这里给我们提供了一次容错的机会,
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;
}
// No implementation found, and method resolver didn't help.
// Use forwarding.
//如果所有的方法都找不到,就会来到这里
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
done:
runtimeLock.unlock();
return imp;
}
这里是通过getMethodNoSuper_nolock进行方法列表查找
getMethodNoSuper_nolock(Class cls, SEL sel)
{
runtimeLock.assertLocked();
assert(cls->isRealized());
// fixme nil cls?
// fixme nil sel?
for (auto mlists = cls->data()->methods.beginLists(),
end = cls->data()->methods.endLists();
mlists != end;
++mlists)
{
method_t *m = search_method_list(*mlists, sel);
if (m) return m;
}
return nil;
}
用的二分法快速的查找方法
static method_t *findMethodInSortedMethodList(SEL key, const method_list_t *list)
{
assert(list);
const method_t * const first = &list->first;
const method_t *base = first;
const method_t *probe;
uintptr_t keyValue = (uintptr_t)key;
uint32_t count;
for (count = list->count; count != 0; count >>= 1) {
probe = base + (count >> 1);
uintptr_t probeValue = (uintptr_t)probe->name;
if (keyValue == probeValue) {
// `probe` is a match.
// Rewind looking for the *first* occurrence of this value.
// This is required for correct category overrides.
while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
probe--;
}
return (method_t *)probe;
}
if (keyValue > probeValue) {
base = probe + 1;
count--;
}
}
return nil;
}
如果所有的方法都找不到,就会来到这里
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;
}
// No implementation found, and method resolver didn't help.
// Use forwarding.
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
首先会进行动态方法解析_class_resolveMethod,在这个过程中,系统会调用一次已经存在的事先定义好的两个类方法,在这里给我们提供了一次容错的机会。首先会判断cls是不是元类,如果cls不是元类的话,说明调用的是实例方法,那就就会调用_class_resolveInstanceMethod函数,如果是元类的话,说明调用的是类方法,那么就会调用_class_resolveClassMethod函数,并且调用完后会再次查找一下sel的指针,找到了就会返回,如果还是找不到的话会调用_class_resolveInstanceMethod函数,这里可以用上面的isa走位图解释:
类方法 - 自己没有 - 父类也没有 - 找父类的父类 - NSObject也没有 - 但是有对象方法 - 返回NSObject的实例方法
void _class_resolveMethod(Class cls, SEL sel, id inst)
{
if (! cls->isMetaClass()) {
// try [cls resolveInstanceMethod:sel]
_class_resolveInstanceMethod(cls, sel, inst);
}
else {
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
_class_resolveClassMethod(cls, sel, inst);
if (!lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
_class_resolveInstanceMethod(cls, sel, inst);
}
}
}
如果我们没有做任何处理,那么则进入消息转发阶段,消息转发阶段全局搜索一下该方法。在其汇编方法内部,调用了__objc_msgForward
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
全局搜索_objc_msgForward_impcache,进入到汇编
屏幕快照 2020-03-08 上午11.05.17.png
然后看到下面执行方法_objc_forward_handler,继续全局搜索,看到的熟悉的找不到方法的报错信息
void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
__attribute__((noreturn)) void
objc_defaultForwardHandler(id self, SEL sel)
{
_objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
"(no message forward handler is installed)",
class_isMetaClass(object_getClass(self)) ? '+' : '-',
object_getClassName(self), sel_getName(sel), self);
}
总结
OC的消息机制可以分为一下三个阶段:
消息发送阶段:先是通过objc_msgSend的快速缓存查找,之后再通过对类以及父类的方法列表进行二分法常规查找。
动态解析阶段:如果消息发送阶段没有找到方法,则会进入动态解析阶段,负责动态的添加方法实现;
消息转发阶段:如果也没有实现动态解析方法,则会进行消息转发阶段,将消息转发给可以处理消息的接受者来处理,消息转发处理失败之后则报错crash。
之后便是消息转发的功能了,这里先附上流程图,下篇我们再继续分析转发的流程。
消息转发流程.png
网友评论