消息查找流程

上节我们分析了objc_msgSend的快速查找部分(汇编部分),这一节我们来分析其慢速部分,也就是c语言部分。

接下来我们通过在代码中打断点来跟进一下这个流程。

image.png

我们将断点的显示模式改为汇编显示。


image.png

然后将代码运行起来。


image.png

我们可以很清楚的看到,对象在调用方法时,底层是通过调用objc_msgSend函数来发送消息的,我们在objc_msgSend函数处打上断点,继续跟进去看看。
image.png

我们可以清楚的看到,objc_msgSend在缓存没有命中之后调用了_objc_msgSend_uncached。

_objc_msgSend_uncached

image.png

_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。

你可能感兴趣的:(消息查找流程)