OC底层原理11 - 动态方法解析

动态方法解析

之前在分析objc_msgSend的时候,当慢速查找和快速查找都未找到SEL时,会根据当前设置的条件(behavior & LOOKUP_RESOLVER)选择是否进行动态方法解析,接下来我们就来探索一下动态方法解析的源码

resolveMethod_locked 源码

static NEVER_INLINE IMP
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
    // 重新在当前类中查找SEL方法
    return lookUpImpOrForwardTryCache(inst, sel, cls, behavior);
}

从以上代码看到,主要分为两种可能:1、当前类为非元类,则调用resolveInstanceMethod方法进行动态解析;2、当前类为元类,则调用resolveClassMethod方法进行动态解析。

resolveInstanceMethod

resolveInstanceMethod源码

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

    //在当前类的缓存列表中查找 resolveInstanceMethod: 方法
    // 查找 resolveInstanceMethod: 方法仍然采用快速查找和慢速查找的流程
    if (!lookUpImpOrNilTryCache(cls, resolve_sel, cls->ISA(/*authenticated*/true))) {
        // Resolver not implemented.
        return;
    }

    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    //执行 [cls resolveInstanceMethod:sel]方法
    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
    //重新按快速查找,慢速查找的方法,查找SEL
    IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
}

resolveInstanceMethod 说明

  • 当前inst:HQPerson对象
  • 当前类cls:HQPerson类
  • 当前类的isa指向:HQPerson元类
  • 由于resolveInstanceMethod方法是类方法,因此主要是去当前类的isa所指向的类中查找是否有该方法实现。
  • 查找resolveInstanceMethod方法的顺序:当前元类的缓存列表->当前元类的方法列表->根元类的缓存列表->根元类的方法列表->根类的缓存列表->根类的方法列表
  • resolveInstanceMethod是当经过快速慢速查找过程后仍然找不到SEL对应的IMP时,苹果提供给我们一次挽救的机会.可以在该方法进对找不到的SEL进行动态的方法添加.若在该函数中进行了挽救,则在下次进行SEL方法查找时,就能返回IMP了.否则就会跳转至_objc_msgForward_impcache.
  • 重点: 对象 ---isa---> 类(对象的实例方法) ---isa---> 元类(对象的类方法/类的实例方法)

resolveClassMethod

resolveClassMethod源码

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

    //查找类中是否有resolveClassMethod:实现
    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);
}

说明

  • 当前inst:HQPerson类
  • 当前类cls:HQPerson元类
  • 当前类的isa指向:根元类
  • resolveClassMethod方法是类方法,由于当前类已经是元类,因此在当前类中查找是否有该方法实现。
  • 查找resolveClassMethod方法的顺序:当前类的缓存列表->当前类的方法列表->根元类的缓存列表->根元类的方法列表->根类的缓存列表->根类的方法列表
  • resolveClassMethod是当经过快速慢速查找过程后仍然找不到SEL对应的IMP时,苹果提供给我们一次挽救的机会.可以在该方法进对找不到的SEL进行动态的方法添加.若在该函数中进行了挽救,则在下次进行SEL方法查找时,就能返回IMP了.
  • 若经过前面的一次挽救仍然未找到SEL对应的IMP,则根据前面对象方法查找的思路,可以通过resolveInstanceMethod函数再进行一次挽救.此时resolveInstanceMethod方法即是类的类方法,因此需要从根元类查找.
  • 重点: 类 ---isa---> 元类(类的实例方法) ---isa---> 根元类(类的类方法/元类的实例方法)

总结

  • 【步骤1】定义一个SEL=resolveClassMethod的方法,并当前类中查找resolveClassMethod方法是否有实现,若未实现,则动态解析的方法直接返回,进入【步骤4】】。
  • 【步骤2】找到了动态解析方法(resolveClassMethod)IMP,执行该方法。
  • 【步骤3】重新在当前类中查找SEL=sel方法。
    • 此时若当前类仍找到SEL,则返回当前SELIMP
    • 此时若当前类仍未找到SEL,则会往当前类cache中将sel方法的IMP置为_objc_msgForward_impcache的IMP,并将结果返回nil。注意:_objc_msgForward_impcache即为最终的未找到函数实现的报错打印。
  • 【步骤4】判断一下当前类中是否有SEL对应的IMP,且IMP不为_objc_msgForward_impcache
    • 当前类中存在SEL对应的IMP且不为_objc_msgForward_impcache,则进入【步骤5】。
    • 当前类中存在SEL对应的IMP_objc_msgForward_impcache,则进入resolveInstanceMethod流程。
      • 【步骤4.1】定义一个SEL=resolveInstanceMethod的方法,并当前类的isa所指向的类(根元类)中查找resolveInstanceMethod方法是否有实现,若未实现,则动态解析的方法直接返回,进入【步骤5】】。
      • 【步骤4.2】找到了动态解析方法(resolveInstanceMethod)IMP,执行该方法。
      • 【步骤4.3】重新在当前类中查找SEL=sel方法。
        • 此时若当前类仍找到SEL,则返回当前SELIMP
        • 此时若当前类仍未找到SEL,则会往当前类cache中将sel方法的IMP置为_objc_msgForward_impcache的IMP,并将结果返回nil。注意:_objc_msgForward_impcache即为最终的未找到函数实现的报错打印。
  • 【步骤5】此时在resolveMethod_locked函数中的最后一步,在当前类中查找SEL。由于经过【步骤3】和【步骤4】,所以此时一定会查找至SEL对应的IMP。存在两种可能:
    • SEL方法的IMP
    • _objc_msgForward_impcache方法的IMP

resolveMethod_locked 总结

  • 对象 ----isa---> 类(对象的实例方法) ---isa---> 元类(对象的类方法/类的实例方法) ---isa---> 根元类(类的类方法/元类的实例方法)
  • 对象方法:
    • 提供一次挽救的机会,即在resolveInstanceMethod方法中进行挽救.
    • 根据对象的类方法存在元类中这个原理,resolveInstanceMethod方法的查找顺序:元类->根元类->根类->nil.
  • 类方法:
    • 提供二次挽救的机会,第一次在resolveClassMethod方法中进行挽救.
    • resolveClassMethod方法的查找顺序:元类->根元类->根类->nil.
    • 第二次在resolveInstanceMethod方法中进行挽救.
    • 根据类的类方法存在根元类中这个原理,resolveInstanceMethod方法的查找顺序:根元类->根类->nil.
  • 对象方法和类方法的共同点,即,都会查找根类中的resolveInstanceMethod方法.因此,可以根类中resolveInstanceMethod函数对类方法/对象方法统一处理.
+(BOOL)resolveInstanceMethod:(SEL)sel {
//    NSLog(@"动态添加%@方法", NSStringFromSelector(sel));
    if (sel == NSSelectorFromString(@"say666")) {
        NSLog(@"%@ 来了", NSStringFromSelector(sel));
        //获取sayMaster方法的imp
        IMP imp = class_getMethodImplementation(self, NSSelectorFromString(@"instanceMethod"));
        //获取sayMaster的实例方法
        Method sayMethod  = class_getInstanceMethod(self, NSSelectorFromString(@"instanceMethod"));
        //获取sayMaster的丰富签名
        const char *type = method_getTypeEncoding(sayMethod);
        //将sel的实现指向sayMaster
        class_addMethod(self, sel, imp, type);
        return YES;
    }
    else if (sel == NSSelectorFromString(@"sayNB")) {
        NSLog(@"%@ 来了", NSStringFromSelector(sel));
        //获取sayMaster方法的imp
        IMP imp = class_getMethodImplementation(objc_getMetaClass("HQPerson"), NSSelectorFromString(@"classMethod"));
        //获取sayMaster的实例方法
        Method sayMethod  = class_getInstanceMethod(objc_getMetaClass("HQPerson"), NSSelectorFromString(@"classMethod"));
        //获取sayMaster的丰富签名
        const char *type = method_getTypeEncoding(sayMethod);
        //将sel的实现指向sayMaster
        class_addMethod(objc_getMetaClass("HQPerson"), sel, imp, type);
        return YES;
    }
    return NO;
}

你可能感兴趣的:(OC底层原理11 - 动态方法解析)