iOS 消息查找流程

297 阅读3分钟

方法的调用

方法响应

无法响应,控制台会打印出最经典的错误

unrecognized selector sent to instance

我们来通过源码调试看下方法的调用流程,为什么会报unrecognized selector

objc_msgSend发送消息进入到了汇编,然后通过

 CacheLookup---> CheckMiss---> __objc_msgSend_uncached---> MethodTableLookup---> __class_lookupMethodAndLoadCache3

__class_lookupMethodAndLoadCache3 这之前的流程我们称之为快速查找,

__class_lookupMethodAndLoadCache3(慢速查找,查找存在类的方法) 我们jump下看下源码,

[student performSelector:@selector(saySomething)];

  IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel,  Class cls)
{
return lookUpImpOrForward(cls, sel, obj, 
                          YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
 }

我们通过断点调试,看下obj,sel,cls 分别为什么?

更换下调用方式 [student sayHello];

 cls:如果是实例方法那就是类,如果是类方法那就是元类
 
 sel:方法名
 
 obj:方法调用者

接下来我们看下 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
if (cache) {
    imp = cache_getImp(cls, sel);
    if (imp) return imp;
}
runtimeLock.lock();
checkIsKnownClass(cls);

if (!cls->isRealized()) {
    realizeClass(cls);
}

1、我们发现进入后会先从缓存再找一次,找到的话就直接返回了,

2、然后对类是否加载完进行判断,如果没有加载完就再加载一遍,

retry:    
 runtimeLock.assertLocked();

// Try this class's cache.

imp = cache_getImp(cls, sel);
if (imp) goto done;

// 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.
        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;
        }
    }
}

getMethodNoSuper_nolock 在当前的class里面找

static method_t *
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;
}

在methodslist 循环遍历查找(二分查找)

  if (meth) {
        log_and_fill_cache(cls, meth->imp, sel, inst, cls);
        imp = meth->imp;
        goto done;
    }

如果找打了, log_and_fill_cache,填充到缓存中,方便下次查找!没找到 // Try superclass caches and method lists. 开始找老爹了

     // 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;
            }
        }

在父类的缓存查找imp,查到就log_and_fill_cache,没有就开始在父类方法列表找(这是逐级查找)

  Method meth = getMethodNoSuper_nolock(curClass, sel);
        if (meth) {
            log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
            imp = meth->imp;
            goto done;
        }

找到就继续填充log_and_fill_cache,如果依然没有找到 开始走消息转发流程,实例方法会转发+(BOOL) resolveInstanceMethod:(SEL)sel;类方法会转发+(BOOL) resolveClassMethod:(SEL)sel,并且类方法转发完后会再次走查找流程,如果还没找到的话会走一下实例方法转发流程;转发逻辑完成后,会再次走一下方法查找(防止动态添加),依然没有处理成功,那么会进行下面

// No implementation found, and method resolver didn't help. 
// Use forwarding.

imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);




   STATIC_ENTRY __objc_msgForward_impcache

         // No stret specialization.
    	 b	__objc_msgForward

	END_ENTRY __objc_msgForward_impcache
	// Default forward handler halts the process.

 __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);
}

unrecognized selector sent to instance 找到报错的源头

总结

快速查找-> 慢速查找 ->消息转发(快速转发-> 慢速转发)

消息转发详解见下篇