iOS 方法查找流程探索总结

断点调试找方法
  1. 断点调试创建People 类对象调用方法 在这里插入图片描述
  2. 按住control键一步步调试 最后会发现如下图所示 这里会调用objc_msgSend在这里插入图片描述
    继续按下control 往下走 回来到 接下来就看看_class_lookupMethodAndLoadCache3的方法实现在这里插入图片描述
_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()) {
        realizeClass(cls);
    }
    if (initialize  &&  !cls->isInitialized()) {
        runtimeLock.unlock();
        _class_initialize (_class_getNonMetaClass(cls, inst));
        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);
    if (imp) goto done;
    // Try this class's method lists.
    {
        Method meth = getMethodNoSuper_nolock(cls, sel);
        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.

    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.

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

 done:
    runtimeLock.unlock();

    return imp;
}

分析

  1. IMP lookUpImpOrForward(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver) 参数解读 cls 当前类 sel 当前方法的名字 inst 当前类对象 initialize 是否初始化 cache 是否缓存 resolver 是否在执行动态方法解析
  2. 如果缓存命中 直接返回imp
if (cache) {
       imp = cache_getImp(cls, sel);
       if (imp) return imp;
   }
  1. 下边来看 realizeClass 我摘取主要的几行代码
static Class realizeClass(Class cls)
{
   runtimeLock.assertLocked();

   const class_ro_t *ro;  // 方法列表 属性列表 等的存储结构题
   class_rw_t *rw; // 存储 class_ro_t的结构题
   Class supercls; // 父类
   Class metacls; // 元类
   bool isMeta; // 当前类是否是元类
   ro = (const class_ro_t *)cls->data();  // 给ro赋值
   isMeta = ro->flags & RO_META; // 是否是元类判断
   supercls = realizeClass(remapClass(cls->superclass)); // 初始化父类
   metacls = realizeClass(remapClass(cls->ISA())); // 初始化元类
   cls->superclass = supercls; 找到当前类的 父类
   cls->initClassIsa(metacls); 初始化当前类的元类
   if (supercls) { // 如果有父类就把当前类关联到父类的子类列表中
       addSubclass(supercls, cls);
   } else {
       addRootClass(cls);
   }
  1. 如果类没有初始化就执行初始化 _class_initialize 的代码就不放了 主要作用就是 没有初始化的类进行初始化 如果有父类并且父类也没有初始化就连 父类也初始化掉 如果初始化完了就什么也不做 如果正在初始化 就等待
if (initialize  &&  !cls->isInitialized()) {
       runtimeLock.unlock();
       _class_initialize (_class_getNonMetaClass(cls, inst));
       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
   }
  1. 开始找方法 存储缓存
{
       Method meth = getMethodNoSuper_nolock(cls, sel); // 一个for循环 从方法列表中找方法
       if (meth) { // 如果找到方法 开始缓存 
           log_and_fill_cache(cls, meth->imp, sel, inst, cls); // 这里会调用 cache_fill 上一篇已经写过
           imp = meth->imp;
           goto done; //  找到直接返回
       }
   }
  1. 如果当前类没找到 就查找父类以及父类的父类 并且执行缓存 如果找到直接返回
for (Class curClass = cls->superclass;
            curClass != nil;
            curClass = curClass->superclass)
  1. 如果以上的过程都没有找到方法 那么就执行动态方法决议 也就是 if (resolver && !triedResolver) {这个判断体中的内容
  2. _class_resolveMethod 方法实现
void _class_resolveMethod(Class cls, SEL sel, id inst)
{
   // 如果不是元类 说明是类 此时类中已经没有方法 直接执行  _class_resolveInstanceMethod
   if (! cls->isMetaClass()) {
       // try [cls resolveInstanceMethod:sel]

       _class_resolveInstanceMethod(cls, sel, inst); //  执行 + (BOOL)resolveInstanceMethod:(SEL)sel方法
   }
   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); // 
       }
   }
}

分析:
(1) 不是元类就找当前类的元类中是否有resolveInstanceMethod 如果没有直接返回 如果有就发送 resolveInstanceMethod 看看有没有动态添加方法 接着再执行方法查找流程
(2)是元类的时候: cls 就是元类 inst 类对象 这时会做两次尝试 第一次检查元类中是否有resolveClassMethod如果没有直接返回,如果有就直接发送resolveClassMethod消息再执行方法查找。如果resolveClassMethod方法没有动态添加方法就再执行一次 _class_resolveInstanceMethod 做一次容错,此时就会找到元类的元类就是根元类,再执行方法查找流程。最后会执行到NSObject的resolveInstanceMethod中。

  1. _class_resolveInstanceMethod方法源代码
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*/);
}
  1. 动态方法解析
    实例方法
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    NSLog(@"------resolveInstanceMethod---");
    if (sel == @selector(happyNewYear)){
        IMP imp = class_getMethodImplementation([self class], @selector(sayHello));
        return class_addMethod(self, sel, imp, "v@:");
    }
    return [super resolveInstanceMethod:sel];
}

类方法

+ (BOOL)resolveClassMethod:(SEL)sel{
    // 因为类方法在元类中查找 所以这里要向元类中添加类方法
    if (sel == @selector(happySpring)){
        IMP imp = class_getMethodImplementation(objc_getMetaClass("People"), @selector(sayGoodBy));
        class_addMethod(objc_getMetaClass("People"), sel, imp, "v@:");
    }
    return [super resolveClassMethod:sel];
}
  1. 消息转发-重定向接收者
    实例方法
- (id)forwardingTargetForSelector:(SEL)aSelector{
    if (aSelector == @selector(happyNewYear)){
        return [[Man alloc]init];
    }
    return [super forwardingTargetForSelector:aSelector];
}

类方法

+ (id)forwardingTargetForSelector:(SEL)aSelector{
    if (aSelector == @selector(happySpring)){
        return [Man class];
    }
    return [super forwardingTargetForSelector:aSelector];
}
  1. 消息转发-方法签名
    实例方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    if (aSelector == @selector(happyNewYear)){
        return [[Man new] methodSignatureForSelector:aSelector];
    }
    return [super methodSignatureForSelector:aSelector];

}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
    [anInvocation invokeWithTarget:[Man new]];
}

类方法

+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    if (aSelector == @selector(happySpring)){
        return [Man methodSignatureForSelector:aSelector];
    }
    return [super methodSignatureForSelector:aSelector];
    
}
+ (void)forwardInvocation:(NSInvocation *)anInvocation{
    [anInvocation invokeWithTarget:[Man class]];
}
总结

iOS中方法的查找分为两部分 第一部分就是快速查找 在缓存中直接找到 发送消息,如果没找到就到了慢速查找 先找当前类 再找父类 已经父类的父类 如果找到就执行 如果找不到就执行动态方法解析和消息转发。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值