ios 底层 方法调用 动态方法解析

403 阅读3分钟

前言

上篇文章中,我们看到在方法的慢速查找过程中,如果在缓存和方法列表中都没有找到的话,首先会调用动态方法解析,今天我们就看动态方法解析过程。

源码

IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool 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. 
    // 消息转发

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

 done:
    runtimeLock.unlock();

    return imp;
}

从源码可以看出,如果动态方法解析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);
        }
    }
}

如果cls 不是元类,走class_resolveInstanceMethod,否则走class_resolveClassMethod这个方法

/***********************************************************************
* _class_resolveInstanceMethod
* Call +resolveInstanceMethod, looking for a method to be added to class cls.
* cls may be a metaclass or a non-meta class.
* Does not check if the method already exists.
**********************************************************************/
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);

    // 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*/);

    // 。。。。省略部分代码
}
/***********************************************************************
* _class_resolveClassMethod
* Call +resolveClassMethod, looking for a method to be added to class cls.
* cls should be a metaclass.
* Does not check if the method already exists.
**********************************************************************/
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 (! lookUpImpOrNil()这一个条件判断,主要是防止程序员写的类 不是继承自NSObject,就不需要开始动态方法解析流程了。
  • (typeof(msg))objc_msgSend,一个objc_msgSend消息发送SEL_resolveInstanceMethod或者SEL_resolveClassMethod。
  • 在代码的注释段我们可以看到resolveInstanceMethod或者resolveClassMethod这么两个方法,意思就是告诉我们可以自己去实现父类NSObject的方法。
  • 对象方法使用的是cls,而类方法使用的是class_getNonMetaClass元类

我们继续看lookUpImpOrNil,源码:

/***********************************************************************
* lookUpImpOrNil.
* Like lookUpImpOrForward, but returns nil instead of _objc_msgForward_impcache
**********************************************************************/
IMP lookUpImpOrNil(Class cls, SEL sel, id inst, 
                   bool initialize, bool cache, bool resolver)
{
    IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);
    if (imp == _objc_msgForward_impcache) return nil;
    else return imp;
}

动态方法解析后,又回到方法慢速查找流程。

代码实现

  • 首先定义一个类
@interface LGStudent : LGPerson
- (void)sayHello;
+ (void)sayObjc;
@end

@implementation LGStudent
- (void)sayHello{
    NSLog(@"%s",__func__);
}
+ (void)sayObjc{
    NSLog(@"%s",__func__);
}
  • 添加动态解析
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    
    if (sel == @selector(saySomething)) {
        NSLog(@"说话了");
        
        IMP sayHIMP = class_getMethodImplementation(self, @selector(sayHello));
    
        Method sayHMethod = class_getInstanceMethod(self, @selector(sayHello));
        
        const char *sayHType = method_getTypeEncoding(sayHMethod);
        
        return class_addMethod(self, sel, sayHIMP, sayHType);
    }
    
    return [super resolveInstanceMethod:sel];
}
  • 验证
        LGStudent *student = [[LGStudent alloc] init];
         [student performSelector:@selector(saySomething)];

输出结果为:

2020-01-06 10:24:42.225756+0800 LGTest[3568:368647] 说话了
2020-01-06 10:24:42.226262+0800 LGTest[3568:368647] -[LGStudent sayHello]

我们在看下类方法的动态解析

+ (BOOL)resolveClassMethod:(SEL)sel{
    
    if (sel == @selector(sayLove)) {
        
        IMP imp = class_getMethodImplementation(objc_getMetaClass("LGStudent"), @selector(sayObjc));
        
         Method method = class_getClassMethod(objc_getMetaClass("LGStudent"), @selector(sayObjc));
        
        const char *types = method_getTypeEncoding(method);
        
        return class_addMethod(objc_getMetaClass("LGStudent"), sel, imp, types);
    }
    return [super resolveClassMethod:sel];
}

类方法是在元类中存储的,因此,添加方法的时候需要添加到类的元类里面

验证:

 [LGStudent performSelector:@selector(sayLove)];

输出内容为: 2020-01-06 10:31:54.084636+0800 LGTest[3757:389847] +[LGStudent sayObjc]

总结

  • 方法动态解析分为实例方法动态解析和类方法动态解析
  • 实例方法的话需要实现+ (BOOL)resolveInstanceMethod:(SEL)sel,并且把方法添加到类中
  • 类方法的需要实现+ (BOOL)resolveClassMethod:(SEL)sel,并且把方法添加到元类中
  • 如果实现了动态方法解析,那么会重新走方法查找(慢速)流程。