RunTime源码阅读(六)之消息查找

1.消息查找

消息查找分为两部分:一部分是汇编,一部分是缓存查找部分。由于功底不够,本文介绍缓存查找部分。


RunTime源码阅读(六)之消息查找_第1张图片
message查找
MessageSend * send = [MessageSend new];
[send message];

直接在方法上加断点,单点跟踪是追踪不了的,我直接找到lookUpImpOrForward方法,在message断点后再打开断点。

_objc_msgSend_uncached是汇编的代码,
MethodTableLookup NORMAL调用了_class_lookupMethodAndLoadCache3

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

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()) {//如果sel中的cls没有实现
        realizeClass(cls);//实现cls
    }
//用到才初始化
    if (initialize  &&  !cls->isInitialized()) {
        runtimeLock.unlock();
        _class_initialize (_class_getNonMetaClass(cls, inst));//初始化cls
        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);//汇编缓存查找:从cls缓存查找sel
    if (imp) goto done;

    // Try this class's method lists.
    {
        Method meth = getMethodNoSuper_nolock(cls, sel);//C语言查找:从methods中
        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;
            }
        }
    }

    // No implementation found. Try method resolver once.
    //方法没有实现,尝试resolver
    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.
    //即没有实现,resolver也不能解决时
    imp = (IMP)_objc_msgForward_impcache;//快速消息转发
    cache_fill(cls, sel, imp, inst);

 done:
    runtimeLock.unlock();

    return imp;
}
  1. 在当前类查找
    cache为NO
    cls->isRealized:判断cls是否实现,没有实现就实现
    cls->isInitialized:判断cls是否初始化,没有初始化就初始化
    cache_getImp:汇编查找:找到直接返回
    getMethodNoSuper_nolock:从cls的data中查找
    log_and_fill_cache:找到填充到bucket中
  2. 父类中查找
    如果以上没找到:沿着superClass在父类查找
  3. 尝试解决
    如还是没有找到:尝试用_class_resolveMethod查找
    如果还没有实现:_objc_msgForward_impcache解决

查找流程:现在当前类查找,再在父类查找,找不到调用_class_resolveMethod方法,还没有解决会调用_objc_msgForward_impcache。其中只要有一步找到,就会立刻停止且方法添加到缓存中。

2. _class_resolveMethod

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


static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst)
{
    if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls, 
                         NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
    {
        // Resolver not implemented.
        return;
    }

    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);//通过objc_msgSend调用类中的+ (BOOL)resolveInstanceMethod:(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 = lookUpImpOrNil(cls, sel, inst, 
                             NO/*initialize*/, YES/*cache*/, NO/*resolver*/);

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

static void _class_resolveClassMethod(Class cls, SEL sel, id inst)
{
    assert(cls->isMetaClass());

    if (! lookUpImpOrNil(cls, SEL_resolveClassMethod, inst, 
                         NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
    {
        // Resolver not implemented.
        return;
    }

    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(_class_getNonMetaClass(cls, inst), 
                        SEL_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 = lookUpImpOrNil(cls, sel, inst, 
                             NO/*initialize*/, YES/*cache*/, NO/*resolver*/);

    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));
        }
    }
}
  1. 实例方法调用_class_resolveInstanceMethod,类方法调用_class_resolveClassMethod。
  2. _class_resolveClassMethod直接通过objc_msg_send 调用了resolveInstanceMethod方法
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);//通过objc_msgSend调用类中的+ (BOOL)resolveInstanceMethod:(SEL)sel方法
  1. 类方法雷同,调用了resolveClassMethod方法。
    而这两个方法是需要开发者自行处理的。

3.log_and_fill_cache

找到的方法填充到缓存

log_and_fill_cache(Class cls, IMP imp, SEL sel, id receiver, Class implementer)
{
#if SUPPORT_MESSAGE_LOGGING
    if (objcMsgLogEnabled) {//log日志
        bool cacheIt = logMessageSend(implementer->isMetaClass(), 
                                      cls->nameForLogging(),
                                      implementer->nameForLogging(), 
                                      sel);
        if (!cacheIt) return;
    }
#endif
    cache_fill (cls, sel, imp, receiver);
}

void cache_fill(Class cls, SEL sel, IMP imp, id receiver)
{
#if !DEBUG_TASK_THREADS
    mutex_locker_t lock(cacheUpdateLock);
    cache_fill_nolock(cls, sel, imp, receiver);
#else
    _collecting_in_critical();
    return;
#endif
}

//添加方法缓存
static void cache_fill_nolock(Class cls, SEL sel, IMP imp, id receiver)
{
    cacheUpdateLock.assertLocked();

    // Never cache before +initialize is done
    if (!cls->isInitialized()) return;

    // Make sure the entry wasn't added to the cache by some other thread 
    // before we grabbed the cacheUpdateLock.
    if (cache_getImp(cls, sel)) return;

    cache_t *cache = getCache(cls);//取出cls的cache:每一个cls都有一个cache_t
    cache_key_t key = getKey(sel);

    // Use the cache as-is if it is less than 3/4 full
    mask_t newOccupied = cache->occupied() + 1;
    mask_t capacity = cache->capacity();
    if (cache->isConstantEmptyCache()) {
        // Cache is read-only. Replace it.
        cache->reallocate(capacity, capacity ?: INIT_CACHE_SIZE);//没有分配,分配内存
    }
    else if (newOccupied <= capacity / 4 * 3) {
        // Cache is less than 3/4 full. Use it as-is.
    }
    else {
        // Cache is too full. Expand it.
        cache->expand();//缓存满了,扩展
    }

    // Scan for the first unused slot and insert there.
    // There is guaranteed to be an empty slot because the 
    // minimum size is 4 and we resized at 3/4 full.
    bucket_t *bucket = cache->find(key, receiver);
    if (bucket->key() == 0) cache->incrementOccupied();
    bucket->set(key, imp);//添加imp缓存
}
  1. cache_t *cache = getCache(cls);获取cls的cache_t
  2. 内存分配
  • cache->isConstantEmptyCache(),cache->reallocate分配内存
  • newOccupied小于3/4,不用操作
  • newOccupied大于等于3/4,按原来2倍大小重新分配内存
  1. 设置bucket_t的缓存

小结:

  1. _objc_msgForward_impcache这个断点跟踪不了,没法看具体逻辑。
  2. 消息查找机制符合预期,可以思考下以下几个问题:
  • 问题:如果父类与子类同时实现_class_resolveMethod,最后会走哪个?
    _class_resolveMethod在父类查找后面实现,肯定是走父类方法不会走_class_resolveMethod
  • 问题2:父类中的实例方法self指的是子类的实例对象还是父类实例对象?
    如果是子类有实现,self指的是子类的实例对象,而所有方法查找都是从子类先开始查找
  • 问题3:父类的按钮有一个点击事件clickAction,而子类也实现了clickAction,最后调用的是哪个?
    看对象是子类对象还是父类对象,如果是子类对象则调用的是子类clickAction,这也是模板模式的经典实用。把钩子留个子类

你可能感兴趣的:(RunTime源码阅读(六)之消息查找)