方法查找下(快速查找)

722 阅读13分钟

引言

前面从汇编的角度分析了objc_msgSend来查找方法的过程,当找不到的时候调用lookUpImpOrForward方法,objc_msgSend是用汇编写的,为什么第一步的缓存查找要用汇编来写呢?后面来查找methodlist的方法又回到了c++代码呢,汇编速度比较快,传入参数的时候,可以动态传入参数,增加了动态性。如果说cache中的查找是快速查找,那么lookUpImpOrForward就是慢速查找。哪里存在methodlist呢,从之前的结构上看cls->data()->methods()中有methods,类存在着继承关系,如果查找的话,会先查找自己的方法列表,然后查找父类的,依次父类查找,对于父类也是先快速查找,然后慢速查找,是一个递归的过程。

源码解析

我们来看lookUpImpOrForward

IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
    const IMP forward_imp = (IMP)_objc_msgForward_impcache;
    IMP imp = nil;
    Class curClass;
    if (slowpath(!cls->isInitialized())) {
        behavior |= LOOKUP_NOCACHE;
     }
     runtimeLock.lock();
     //关于类一些是注册的操作
     checkIsKnownClass(cls);
     cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);
    // runtimeLock may have been dropped but is now locked again
    runtimeLock.assertLocked();
    curClass = cls;
    for (unsigned attempts = unreasonableClassCount();;) {
        if (curClass->cache.isConstantOptimizedCache(/* strict */true)) {
#if CONFIG_USE_PREOPT_CACHES
            imp = cache_getImp(curClass, sel);
            if (imp) goto done_unlock;
            curClass = curClass->cache.preoptFallbackClass();
#endif
        } else {
            // curClass method list.
            Method meth = getMethodNoSuper_nolock(curClass, sel);
            if (meth) {
                imp = meth->imp(false);
                goto done;
            }

            if (slowpath((curClass = curClass->getSuperclass()) == nil)) {
                // No implementation found, and method resolver didn't help.
                // Use forwarding.
                imp = forward_imp;
                break;
            }
        }

        // Halt if there is a cycle in the superclass chain.
        if (slowpath(--attempts == 0)) {
            _objc_fatal("Memory corruption in class list.");
        }

        // Superclass cache.
        imp = cache_getImp(curClass, sel);
        if (slowpath(imp == forward_imp)) {
            // Found a forward:: entry in a superclass.
            // Stop searching, but don't cache yet; call method
            // resolver for this class first.
            break;
        }
        if (fastpath(imp)) {
            // Found the method in a superclass. Cache it in this class.
            goto done;
        }
    }

    // No implementation found. Try method resolver once.

    if (slowpath(behavior & LOOKUP_RESOLVER)) {
        behavior ^= LOOKUP_RESOLVER;
        return resolveMethod_locked(inst, sel, cls, behavior);
    }

 done:
    if (fastpath((behavior & LOOKUP_NOCACHE) == 0)) {
#if CONFIG_USE_PREOPT_CACHES
        while (cls->cache.isConstantOptimizedCache(/* strict */true)) {
            cls = cls->cache.preoptFallbackClass();
        }
#endif
        log_and_fill_cache(cls, imp, sel, inst, curClass);
    }
 done_unlock:
    runtimeLock.unlock();
    if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
        return nil;
    }
    return imp;
    
  }

我们分析一下 cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);这个方法

realizeAndInitializeIfNeeded_locked(id inst, Class cls, bool initialize)
{
    runtimeLock.assertLocked();
    if (slowpath(!cls->isRealized())) {
        cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
        // runtimeLock may have been dropped but is now locked again
    }

    if (slowpath(initialize && !cls->isInitialized())) {
        cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
        // runtimeLock may have been dropped but is now locked again

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

如果没有实现,则走 cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock); realizeClassMaybeSwiftAndLeaveLocked方法中

realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked)
{
    lock.assertLocked();

    if (!cls->isSwiftStable_ButAllowLegacyForNow()) {
        // Non-Swift class. Realize it now with the lock still held.
        // fixme wrong in the future for objc subclasses of swift classes
        realizeClassWithoutSwift(cls, nil);
        if (!leaveLocked) lock.unlock();
    } else {
        // Swift class. We need to drop locks and call the Swift
        // runtime to initialize it.
        lock.unlock();
        cls = realizeSwiftClass(cls);
        ASSERT(cls->isRealized());    // callback must have provoked realization
        if (leaveLocked) lock.lock();
    }

    return cls;
}

我们看Non-Swift classrealizeClassWithoutSwift方法

static Class realizeClassWithoutSwift(Class cls, Class previously)
{
    runtimeLock.assertLocked();

    class_rw_t *rw;
    Class supercls;
    Class metacls;

    if (!cls) return nil;
    if (cls->isRealized()) {
        validateAlreadyRealizedClass(cls);
        return cls;
    }
    ASSERT(cls == remapClass(cls));

    // fixme verify class is not in an un-dlopened part of the shared cache?

    auto ro = (const class_ro_t *)cls->data();
    auto isMeta = ro->flags & RO_META;
    if (ro->flags & RO_FUTURE) {
        // This was a future class. rw data is already allocated.
        rw = cls->data();
        ro = cls->data()->ro();
        ASSERT(!isMeta);
        cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
    } else {
        // Normal class. Allocate writeable class data.
        rw = objc::zalloc<class_rw_t>();
        rw->set_ro(ro);
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
        cls->setData(rw);
    }

    cls->cache.initializeToEmptyOrPreoptimizedInDisguise();

#if FAST_CACHE_META
    if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif

    // Choose an index for this class.
    // Sets cls->instancesRequireRawIsa if indexes no more indexes are available
    cls->chooseClassArrayIndex();

    if (PrintConnecting) {
        _objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
                     cls->nameForLogging(), isMeta ? " (meta)" : "", 
                     (void*)cls, ro, cls->classArrayIndex(),
                     cls->isSwiftStable() ? "(swift)" : "",
                     cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
    }

  
    supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);

    .....

    return cls;
}

可以看到这个方法中做了协议rw,的赋值操作,而且在里面调用了 supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil); metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);就把之前介绍的那个isa以及superClass的图整个给初始化掉了。 我们回到lookUpImpOrForward方法中,看到for (unsigned attempts = unreasonableClassCount();;)这是一个死循环,退出条件和改变条件为空,那么怎么才能跳出这个循环呢,只有在里面有return或者break或者goto这些字段。我们来详细看下里面的内容。 由前面分析知道,如果类没有实现,则会进行一步初始化操作,所以为了正确性,先进行了一步共享缓存的查找。

  if (curClass->cache.isConstantOptimizedCache(/* strict */true)) {
#if CONFIG_USE_PREOPT_CACHES 
            imp = cache_getImp(curClass, sel);
            if (imp) goto done_unlock;
            curClass = curClass->cache.preoptFallbackClass();
#endif
        } 

我们一般类是已经初始化过的,就直接看else部分 这个是获取方法的具体方法。 Method meth = getMethodNoSuper_nolock(curClass, sel); 点进去看一下,最后定位到查找方法的具体函数实现

findMethodInSortedMethodList(SEL key, const method_list_t *list, const getNameFunc &getName)
{
    ASSERT(list);

    auto first = list->begin();
    auto base = first;
    decltype(first) probe;

    uintptr_t keyValue = (uintptr_t)key;
    uint32_t count;
    //5 probe=2
    for (count = list->count; count != 0; count >>= 1) {
        probe = base + (count >> 1);
        
        uintptr_t probeValue = (uintptr_t)getName(probe);
        
        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)getName((probe - 1))) {
                probe--;
            }
            return &*probe;
        }
        
        if (keyValue > probeValue) {
            base = probe + 1;
            count--;
        }
    }
    
    return nil;
}

这是一个二分查找。 比如刚开始 count=8,1000, 第一轮:probe = base + (count >> 1);//count >> 1 = 4, 即probe 4 如果值大于4时候的值 则 base = 5, count = 7, count >>=1 则count = 3(0111 => 0011) 第二轮:probe = base + (count >> 1); //count>>1 = 1, 即probe = 6 则比较6时候的值,直到找到或者找不到结束 如果值小于4时候的值 count>>=1 = 4 第二轮:probe = base + (count >> 1);//4>>1, 则probe = 2

如果找到则走 goto done; 把方法插入到cache中, log_and_fill_cache(cls, imp, sel, inst, curClass);则会进入到我们前面讲到的缓存插入流程 cls->cache.insert(sel, imp, receiver);

done:
    if (fastpath((behavior & LOOKUP_NOCACHE) == 0)) {
#if CONFIG_USE_PREOPT_CACHES
        while (cls->cache.isConstantOptimizedCache(/* strict */true)) {
            cls = cls->cache.preoptFallbackClass();
        }
#endif
        log_and_fill_cache(cls, imp, sel, inst, curClass);
    }

如果没有找到并且父类不为nil,则走 imp = cache_getImp(curClass, sel);

extern "C" IMP cache_getImp(Class cls, SEL sel, IMP value_on_constant_cache_miss = nil);

进行父类的快速查找,

STATIC_ENTRY _cache_getImp
GetClassFromIsa_p16 p0, 0
CacheLookup GETIMP, _cache_getImp, LGetImpMissDynamic, LGetImpMissConstant

缓存中如果没有找到,则走

LGetImpMissDynamic:
mov	p0, #0
ret

如果没有找到,继续循环,走
Method meth = getMethodNoSuper_nolock(curClass, sel);查找父类方法列表,依次递归查找父类直到nil。

没有找到赋值错误信息

如果都没有找到则 imp = forward_imp; 将forward_imp赋值为imp

 if (slowpath((curClass = curClass->getSuperclass()) == nil)) {
                // No implementation found, and method resolver didn't help.
                // Use forwarding.
                imp = forward_imp;
                break;
            }

我们看一下forward_imp

  const IMP forward_imp = (IMP)_objc_msgForward_impcache;
       STATIC_ENTRY __objc_msgForward_impcache

	// No stret specialization.
	b	__objc_msgForward

	END_ENTRY __objc_msgForward_impcache

	
	ENTRY __objc_msgForward

	adrp	x17, __objc_forward_handler@PAGE
	ldr	p17, [x17, __objc_forward_handler@PAGEOFF]
	TailCallFunctionPointer x17
	
	END_ENTRY __objc_msgForward

搜索一下__objc_forward_handler

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);
}
void *_objc_forward_handler = (void*)objc_defaultForwardHandler;

动态方法决议

如果么有找到,则会继续往下走

if (slowpath(behavior & LOOKUP_RESOLVER)) {// 3 & 2 = 2
        behavior ^= LOOKUP_RESOLVER;  3 ^ 2 = 2; behavior = 2
        return resolveMethod_locked(inst, sel, cls, behavior);
    }

我们看下behavior的值

// lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
//枚举值如下
enum {
    LOOKUP_INITIALIZE = 1,
    LOOKUP_RESOLVER = 2,
    LOOKUP_NIL = 4,
    LOOKUP_NOCACHE = 8,
};

behavior = 3 // 0001 | 0010 = 0011(3) behavior = 2时,behavior ^= LOOKUP_RESOLVER;// 2 ^2 = 0,就不会进入到判断中了。 我们看下resolveMethod_locked函数

resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
    runtimeLock.assertLocked();
    ASSERT(cls->isRealized());

    runtimeLock.unlock();

    if (! cls->isMetaClass()) { //对象方法
        // try [cls resolveInstanceMethod:sel]
        resolveInstanceMethod(inst, sel, cls);
    } 
    else {
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        resolveClassMethod(inst, sel, cls);
        if (!lookUpImpOrNilTryCache(inst, sel, cls)) {
            resolveInstanceMethod(inst, sel, cls);
        }
    }

    // chances are that calling the resolver have populated the cache
    // so attempt using it
    return lookUpImpOrForwardTryCache(inst, sel, cls, behavior);
}

lookUpImpOrForwardTryCache这个方法调用了_lookUpImpTryCache,而_lookUpImpTryCache又重新走了一次查找。说明系统在

 if (! cls->isMetaClass()) { //对象方法
        // try [cls resolveInstanceMethod:sel]
        resolveInstanceMethod(inst, sel, cls);
    } 
    else {
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        resolveClassMethod(inst, sel, cls);
        if (!lookUpImpOrNilTryCache(inst, sel, cls)) {
            resolveInstanceMethod(inst, sel, cls);
        }
    }

又给了一次添加方法的机会。我们看实例方法走的这个分支resolveInstanceMethod

static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked();
    ASSERT(cls->isRealized());
    SEL resolve_sel = @selector(resolveInstanceMethod:);

    if (!lookUpImpOrNilTryCache(cls, resolve_sel, cls->ISA(/*authenticated*/true))) {
        // Resolver not implemented.
        return;
    }
   //如果我们实现了resolveInstanceMethod:方法,系统就会在走一遍查找,我们可以在这个方法中进行方法的添加
    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls, resolve_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 = lookUpImpOrNilTryCache(inst, sel, cls);
    ...
}

📢resolveInstanceMethod即使类不实现,系统也会有一个实现,返回NO

代码实践:

@interface GoodPerson : NSObject

@end
@implementation GoodPerson
- (void)sayHappy {
    NSLog(@"%s", __func__);
    
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(sayInstance)) {
        IMP sayHappy = class_getMethodImplementation(self, @selector(sayHappy));
        Method sayHapppyM = class_getInstanceMethod(self, @selector(sayHappy));
        const char *type = method_getTypeEncoding(sayHapppyM);
        return class_addMethod(self, sel, sayHappy, type);
    }
    return [super resolveInstanceMethod:sel];
    
}

@end

//调用方

 GoodPerson *p = [GoodPerson alloc];
 [p sayInstance];
 
 //打印
 //-[GoodPerson sayHappy]

我们来看一下类方法的动态决议:

 resolveClassMethod(inst, sel, cls);
  if (!lookUpImpOrNilTryCache(inst, sel, cls)) {
        resolveInstanceMethod(inst, sel, cls);
   }

❓为什么执行完resolveClassMethod之后,又执行了一遍resolveInstanceMethod方法呢,这个就是之前讲的isa走位图,对于类来说 它的继承链是 (class)Subclass->Superclass->Rootclass->nil 类的isa指向元类,元类也是类,类对于元类来说也是实例方法,元类也有一条继承链 (meta)Subclass->Superclass->Rootclass->Rootclass(class)->nil 所以还会走一遍resolveInstanceMethod

可以看到resolveClassMethodresolveInstanceMethod实现差不多

static void resolveClassMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked();
    ASSERT(cls->isRealized());
    ASSERT(cls->isMetaClass());

    if (!lookUpImpOrNilTryCache(inst, @selector(resolveClassMethod:), cls)) {
        // Resolver not implemented.
        return;
    }

    Class nonmeta;
    {
        mutex_locker_t lock(runtimeLock);
        nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
        // +initialize path should have realized nonmeta already
        if (!nonmeta->isRealized()) {
            _objc_fatal("nonmeta class %s (%p) unexpectedly not realized",
                        nonmeta->nameForLogging(), nonmeta);
        }
    }
    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(nonmeta, @selector(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 = lookUpImpOrNilTryCache(inst, sel, cls);
    ...

bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel); resolveClassMethod这个是添加到nonmeta,那么我们应该添加到哪里呢,添加到nsobject上吗?❎,由前面知道,方法在底层都是实例方法,类方法对于元类来说就是元类的实例方法,所以resolveClassMethod是一个➕方法,添加到对应的类文件里面就可以了。

实践

+ (BOOL)resolveClassMethod:(SEL)sel {
    if (sel == @selector(sayClassMethod)) {
        //注意这里是添加到元类中
        IMP sayHappy = class_getMethodImplementation(objc_getMetaClass("GoodPerson"), @selector(sayClassHappy));
        Method sayHapppyM = class_getInstanceMethod(objc_getMetaClass("GoodPerson"), @selector(sayClassHappy));
        const char *type = method_getTypeEncoding(sayHapppyM);
        return class_addMethod(objc_getMetaClass("GoodPerson"), sel, sayHappy, type);
    }
    return [super resolveClassMethod:sel];
}

现在我们是把方法分别写在GoodPerson这个类中,由类的动态决议知道也会走resolveInstanceMethod, 那么我们可以写个NSObject的分类,在resolveInstanceMethod中对两个方法进行实现

实践:

#import "NSObject+Resolve.h"
#import <objc/message.h>

@implementation NSObject (Resolve)
- (void)sayHappy {
    NSLog(@"%s", __func__);
    
}
+ (void)sayClassHappy {
    NSLog(@"%s", __func__);
    
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(sayInstance)) {
        IMP sayHappy = class_getMethodImplementation(self, @selector(sayHappy));
        Method sayHapppyM = class_getInstanceMethod(self, @selector(sayHappy));
        const char *type = method_getTypeEncoding(sayHapppyM);
        return class_addMethod(self, sel, sayHappy, type);
    }
    if (sel == @selector(sayClassMethod)) {
        //注意这里是添加到元类中
        IMP sayHappy = class_getMethodImplementation(objc_getMetaClass("GoodPerson"), @selector(sayClassHappy));
        Method sayHapppyM = class_getInstanceMethod(objc_getMetaClass("GoodPerson"), @selector(sayClassHappy));
        const char *type = method_getTypeEncoding(sayHapppyM);
        return class_addMethod(objc_getMetaClass("GoodPerson"), sel, sayHappy, type);
    }
    
    return NO;
    
}

@end

消息转发

如果动态方法决议这个方法没有重写,我们会走一个快速消息转发

//GoodPerson  调用了GoodPerson中的一个对象方法,但是它没有实现,在Teacher中我们进行了实现
- (id)forwardingTargetForSelector:(SEL)aSelector {
    return [Teacher alloc];
}

返回一个处理这个方法的新的对象,把消息派发给这个新的对象,由新的对象来处理这个方法

If an object implements (or inherits) this method, and returns a non-nil (and non-self) result, that returned object is used as the new receiver object and the message dispatch resumes to that new object. (Obviously if you return self from this method, the code would just fall into an infinite loop.)

📢如果是类方法,则是+方法

//GoodPerson  调用了GoodPerson中的一个类方法,但是它没有实现,在Teacher中我们进行了实现
+ (id)forwardingTargetForSelector:(SEL)aSelector {
    return [Teacher class];
}

快速消息转发没有实现,会走一个慢速消息转发

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
   //返回一个方法签名
    return  [NSMethodSignature signatureWithObjCTypes:"v@:"];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    
    Teacher *t = [Teacher alloc];
    if ([anInvocation.target respondsToSelector:anInvocation.selector]) {
        [anInvocation invoke];
    } else if ([t respondsToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:t];
    } else {
        //可进行一些日志上报操作
        NSLog(@"-方法找不到,日志上报-");
    }
   
}

慢速消息转发更灵活,如果把慢速消息转发写在NSObject中,可以进行一些方法崩溃的日志上传或者一些防方法崩溃操作。 Aspect埋点也是对forwardInvocation进行了操作,后面补充。

总的流程

对于一个对象方法,会先从缓存中查找,缓存中没有,则查找类的方法列表(已排序的采取二分查找的方式来查找),如果没有找到,逐级父类递归查找。父类查找中,也是先查找父类的缓存,然后查找父类的方法列表。如果没有找到会走一个消息转发的流程。

截屏2021-07-04 上午10.02.42.png

补充

resolveInstanceMethod为什么会走两遍
//当我们调用动态方法解析的时候,如果我们没有在动态方法解析里面添加处理方法,不管返回的是YES或者NO,resolveInstanceMethod打印两遍
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    NSLog(@"%s---%@",__func__, NSStringFromSelector(sel));
    return YES; 
}

/* resolveInstanceMethod走两遍
2021-07-04 08:44:24.086211+0800 KCObjcBuild[18321:763151] +[GoodPerson resolveInstanceMethod:]---sayInstance
2021-07-04 08:44:24.087243+0800 KCObjcBuild[18321:763151] -[GoodPerson forwardingTargetForSelector:]---sayInstance
2021-07-04 08:44:24.087390+0800 KCObjcBuild[18321:763151] -[GoodPerson methodSignatureForSelector:]---sayInstance
2021-07-04 08:44:24.087543+0800 KCObjcBuild[18321:763151] +[GoodPerson resolveInstanceMethod:]---_forwardStackInvocation:
*/

❓,为什么会出现上面的情况呢 截屏2021-07-04 上午8.58.38.png 我们打下这三个断点,在第一次打印完resolveInstanceMethod这个函数的时候,使用bt打印堆栈信息。得到如下:

2021-07-04 08:57:25.127457+0800 KCObjcBuild[18422:770975] +[GoodPerson resolveInstanceMethod:]---sayInstance
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
  * frame #0: 0x000000010032a33a libobjc.A.dylib`resolveInstanceMethod(inst="sayInstance", sel="encodeWithOSLogCoder:options:maxLength:", cls=__NSCFString) at objc-runtime-new.mm:6232:38
    frame #1: 0x0000000100313a53 libobjc.A.dylib`resolveMethod_locked(inst="sayInstance", sel="encodeWithOSLogCoder:options:maxLength:", cls=__NSCFString, behavior=4) at objc-runtime-new.mm:6270:9
    frame #2: 0x00000001003126d6 libobjc.A.dylib`lookUpImpOrForward(inst="sayInstance", sel="encodeWithOSLogCoder:options:maxLength:", cls=__NSCFString, behavior=4) at objc-runtime-new.mm:6495:16
    frame #3: 0x00000001003129d0 libobjc.A.dylib`lookUpImpOrNilTryCache [inlined] _lookUpImpTryCache(inst="sayInstance", sel="encodeWithOSLogCoder:options:maxLength:", cls=__NSCFString, behavior=6) at objc-runtime-new.mm:6372:16
    frame #4: 0x0000000100312948 libobjc.A.dylib`lookUpImpOrNilTryCache(inst="sayInstance", sel="encodeWithOSLogCoder:options:maxLength:", cls=__NSCFString, behavior=2) at objc-runtime-new.mm:6389
    frame #5: 0x00000001002f8a8a libobjc.A.dylib`class_respondsToSelector_inst(inst="sayInstance", sel="encodeWithOSLogCoder:options:maxLength:", cls=__NSCFString) at objc-class.mm:646:26
    frame #6: 0x000000010034ff8a libobjc.A.dylib`-[NSObject respondsToSelector:](self="sayInstance", _cmd="respondsToSelector:", sel="encodeWithOSLogCoder:options:maxLength:") at NSObject.mm:2300:12
    frame #7: 0x000000010034f358 libobjc.A.dylib`objc_opt_respondsToSelector(obj="sayInstance", sel="encodeWithOSLogCoder:options:maxLength:") at NSObject.mm:2039:12
    frame #8: 0x00007fff2040bef5 libsystem_trace.dylib`_os_log_fmt_flatten_object + 200
    frame #9: 0x00007fff204097b6 libsystem_trace.dylib`_os_log_impl_flatten_and_send + 2054
    frame #10: 0x00007fff2040ca27 libsystem_trace.dylib`_os_log_with_args_impl + 460
    frame #11: 0x00007fff2079f5fa CoreFoundation`_CFLogvEx3 + 185
    frame #12: 0x00007fff215d9904 Foundation`_NSLogv + 102
    frame #13: 0x00007fff215031bb Foundation`NSLog + 132
    frame #14: 0x0000000100003c15 KCObjcBuild`-[GoodPerson forwardingTargetForSelector:](self=0x000000010064d070, _cmd="forwardingTargetForSelector:", aSelector="sayInstance") + 53 [opt]
    frame #15: 0x00007fff2076f4f4 CoreFoundation`___forwarding___ + 225
    frame #16: 0x00007fff2076f388 CoreFoundation`_CF_forwarding_prep_0 + 120
    frame #17: 0x0000000100003a20 KCObjcBuild`main(argc=<unavailable>, argv=<unavailable>) + 64 [opt]
    frame #18: 0x00007fff206aff3d libdyld.dylib`start + 1
    frame #19: 0x00007fff206aff3d libdyld.dylib`start + 1

可以看到,调用了CoreFoundation中的_CF_forwarding_prep_0 ___forwarding___ 然后中间调用了系统的库,libsystem_trace,最后回到了libobjc的库.

_CF_forwarding_prep_0 ___forwarding___,是慢速消息转发的流程,我们可以下载CoreFoundation的可执行文件,用Hopper工具打开

搜索forwarding看下伪代码形式

截屏2021-07-04 上午9.28.19.png

截屏2021-07-04 上午9.30.16.png

截屏2021-07-04 上午9.30.38.png

我们看到里面调用了一个_forwardStackInvocation:方法,但是在libobjc中并没有找到这个方法,是系统自动调用的。 方法一直没有被处理,会走下面这个方法 截屏2021-07-04 上午9.34.20.png

点进去,看到在下面这个方法中 截屏2021-07-04 上午9.35.46.png

libobjc中的objc_opt_respondsToSelector

查看源码,objc_opt_respondsToSelector->class_respondsToSelector_inst->lookUpImpOrNilTryCache->_lookUpImpTryCache->lookUpImpOrForward又进行了一次查找,所以又调用了resolveInstanceMethod方法

objc_opt_respondsToSelector(id obj, SEL sel)
{
#if __OBJC2__
    if (slowpath(!obj)) return NO;
    Class cls = obj->getIsa();
    if (fastpath(!cls->hasCustomCore())) {
        return class_respondsToSelector_inst(obj, sel, cls);
    }
#endif
    return ((BOOL(*)(id, SEL, SEL))objc_msgSend)(obj, @selector(respondsToSelector:), sel);
}
class_respondsToSelector_inst(id inst, SEL sel, Class cls)
{
    // Avoids +initialize because it historically did so.
    // We're not returning a callable IMP anyway.
    return sel && cls && lookUpImpOrNilTryCache(inst, sel, cls, LOOKUP_RESOLVER);
}
IMP lookUpImpOrNilTryCache(id inst, SEL sel, Class cls, int behavior)
{
    return _lookUpImpTryCache(inst, sel, cls, behavior | LOOKUP_NIL);
}
static IMP _lookUpImpTryCache(id inst, SEL sel, Class cls, int behavior)
{
    runtimeLock.assertUnlocked();

    if (slowpath(!cls->isInitialized())) {
        // see comment in lookUpImpOrForward
        return lookUpImpOrForward(inst, sel, cls, behavior);
    }

    IMP imp = cache_getImp(cls, sel);
    if (imp != NULL) goto done;
#if CONFIG_USE_PREOPT_CACHES
    if (fastpath(cls->cache.isConstantOptimizedCache(/* strict */true))) {
        imp = cache_getImp(cls->cache.preoptFallbackClass(), sel);
    }
#endif
    if (slowpath(imp == NULL)) {
        return lookUpImpOrForward(inst, sel, cls, behavior);
    }

done:
    if ((behavior & LOOKUP_NIL) && imp == (IMP)_objc_msgForward_impcache) {
        return nil;
    }
    return imp;
}

整个消息流程调用完之后,没有找到对应的方法,系统会进行一个处理,调用到libobjc中,重新进行一次查找