Runtime & Runloop

Posted coolcold

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Runtime & Runloop相关的知识,希望对你有一定的参考价值。

方法->底层会编译成消息->消息查找会使用递归查找

元类是一种虚拟的类,系统实现的,用来存储类对象的

对象分为:

1. 实例对象:存在类里面,

2. 类对象:存在元类里面

实例方法:

递归查找父类 -> 最终会查找到NSObject

 

如果没有实现就会进入动态方法解析

/***********************************************************************

* lookUpImpOrForward.

* The standard IMP lookup. 

* initialize==NO tries to avoid +initialize (but sometimes fails)

* cache==NO skips optimistic unlocked lookup (but uses cache elsewhere)

* Most callers should use initialize==YES and cache==YES.

* inst is an instance of cls or a subclass thereof, or nil if none is known. 

*   If cls is an un-initialized metaclass then a non-nil inst is faster.

* May return _objc_msgForward_impcache. IMPs destined for external use 

*   must be converted to _objc_msgForward or _objc_msgForward_stret.

*   If you don‘t want forwarding at all, use lookUpImpOrNil() instead.

**********************************************************************/

IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 

                       bool initialize, bool cache, bool resolver)

{

  调用方法cls首先进来的是类对象。

    IMP imp = nil;

    bool triedResolver = NO;

 

    runtimeLock.assertUnlocked();

 

    // Optimistic cache lookup

    if (cache) {

        imp = cache_getImp(cls, sel);

        if (imp) return imp;

    }

 

    // runtimeLock is held during isRealized and isInitialized checking

    // to prevent races against concurrent realization.

 

    // runtimeLock is held during method search to make

    // method-lookup + cache-fill atomic with respect to method addition.

    // Otherwise, a category could be added but ignored indefinitely because

    // the cache was re-filled with the old value after the cache flush on

    // behalf of the category.

 

    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;

        }

    }

 

  如果自己没有这个方法,就会查找父类的method和caches里面是否有这个方法。

    // Try superclass caches and method lists.

    {

        unsigned attempts = unreasonableClassCount();

        for (Class curClass = cls->superclass;

             curClass != nil;

             curClass = curClass->superclass)

        { 如果父类不为nil(NSObject父类为nil),for循环查找父类,直到NSObject

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

        如果找到了就缓存到chache里面,下次查找方便

            if (meth) {

                log_and_fill_cache(cls, meth->imp, sel, inst, curClass);

                imp = meth->imp;

                goto done;

            }

        }

    }

 

    // No implementation found. Try method resolver once.

   如果都没有找到,就会走动态方法解析_class_resolveMethod

    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;

}

 

动态方法解析

/***********************************************************************

* _class_resolveMethod

* Call +resolveClassMethod or +resolveInstanceMethod.

* Returns nothing; any result would be potentially out-of-date already.

* Does not check if the method already exists.

**********************************************************************/

void _class_resolveMethod(Class cls, SEL sel, id inst)

{

  首先判断是元类吗?目的是区服是对象方法还是类方法(对象方法存在类里面,类方法存在元类里面)

    if (! cls->isMetaClass()) {  

        // try [cls c]

 

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

        }

    }

}

 

 

/***********************************************************************

* _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)

{

  查找当前cls的isa->原类是否实现了resolveInstanceMethod

    if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls, 

                         NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 

    {这一步的目的就是查找是否混乱,防止错误,因为最终会查找到NSObject里面的resolveInstanceMethod:实现,如果的父类里面都没有实现resolveInstanceMethod那么也就没有查找下去的必要性了,如果NSObject里面都没有resolveInstanceMethod,证明这个程序是有问题的,所以直接返回就好了,没有必要进行下层的动态方法解析流程。

        // Resolver not implemented.

        return;

    }

 

  如果上面都没有问题,那么就进行objc_msgSend

    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;

    bool resolved = msg(cls, SEL_resolveInstanceMethod, sel); 返回你的自定义动态方法解析的bool值

 

    // 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*/);重新递归查找sel的imp,所以说汇编之后就会进行漫长的查找过程

 

 

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

        }

    }

}

 

/***********************************************************************

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

{

  查找原类对象是否有这个sel这个方法,又会调用lookUpImpOrForward递归查找是否有实现,这样可能会形成死循环,系统处理方式是在NSObject实现了这个类方法下面有源码截图

    IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);

    if (imp == _objc_msgForward_impcache) return nil如果是消息转发的imp就返回nil

    else return imp; 如果找到了就返回imp

}

 

 

NSObject里面默认实现了这两个方法:(这样就不会形成方法动态解析死循环)

技术图片

 

 

 返回YES说明你已经对方法动态解析做了处理,如果返回NO说明要继续到下层处理。

 

+ (BOOL)resolveInstanceMethod:(SEL)sel {

  //系统会自动发送消息 来到这里

    NSLog(@"来了 老弟 - %@",self);

    if (sel == @selector(run)) {

//        我们动态解析我们的 对象方法

//        NSLog(@"对象方法 run 解析走这里");

        SEL runSEL = @selector(run);

        Method runM= class_getInstanceMethod(self, runSEL);

        IMP runImp = method_getImplementation(runM);

        const char *type = method_getTypeEncoding(runM);

        return class_addMethod(self, sel, runImp, type); 这里只是返回了一个添加成功,但是并没有返回imp,原因是系统会通过sel再找一次imp

    }

    return [super resolveInstanceMethod:sel];

}

 

动态解析方法的实质 通过 sel 查找 imp,系统在崩溃之前会查找下resolveInstanceMethod方法,你有没有处理给没有imp的sel添加imp,如果你添加处理了,系统会再次重新查找imp方法,程序正常执行。

 

 

下面是类方法动态解析:

发现一个问题,在调用类的类方法,在分类里面实现同名实例方法,程序不会崩溃(并且不会走类方法动态解析)。

类方法 -> 首先递归找自己 -> 然后找父类 (如果没找到走动态解析)

 

对象方法的存储在类

类方法的存储在元类

 

类方法首先查找元类里面的对象方法

 

验证了类方法和原类实例方法是同一个方法(地址相同):

技术图片 

 

源码:发现类方法其实里面重新调用了从元类里面去实例方法(与之前预测相符)

技术图片

 

单步调试分类里面实现与类方法同名的单例方法程序不崩溃原因:

 

 

技术图片

 

 技术图片

 

技术图片

 

在查找walk类方法过程中,会查找元类->根元类->NSObject,直到NSObject类是否有walk实例方法,因为NSObject的类有分类walk实例方法所以不会崩溃。和对象方法查找有一点不一样。

技术图片

 

 类方法可以在NSObject里面以类方法和对象方法实现,但是最好是以类方法实现,以类方法实现的话会少找一层。

 因为根类NSObject的类方法是存储在根元类里面,所以在找到根元类的时候就找到了方法,不会再去NSObject的类方法列表里去查找

 技术图片

 

 

 

 

 

以上是关于Runtime & Runloop的主要内容,如果未能解决你的问题,请参考以下文章

Objective-C & Runtime

Runtime & Runloop

iOS开发-Swift进阶之内存管理 & Runtime!

iOS底层探索之Runtime: objc_msgSend&汇编快速查找分析

Runtime之方法

iOS底层探索之Runtime: 动态方法解析