美文网首页
Runtime底层原理之源码分析

Runtime底层原理之源码分析

作者: 越来越胖了 | 来源:发表于2019-08-12 01:20 被阅读0次

    前面两篇放了一张runtime的思维导图,提供了一些runtime的常用API,这一章主要从汇编 、C、 C++源码上分析runtime的底层,后面还会写Runtime开发中的实际应用(构思ing中...)。本篇博客涉及到的demoruntime源码会在博客结尾给出下载链接

    Runtime

    一点半了,我靠,今天不写了,明天还要上班,睡觉去了,狗命要紧......这个文章这周一定找时间更新上去😴😴😴😴😴

    runtime是什么❓

    c,c++,汇编一起写的API,装载到内存给程序提供运行时的功能;编译期runtime下层会被编译成runtime的API。

    runtime的三种调用方式

    1. runtimeAPI
    2. NSObject的API(isKindOf等)
    3. OC的上层(@selector)
    • 注:代码中...代表不去讲解的代码,非重点

    我们从一个简单实例入手,查看runtime的原理;首先创建一个LGPerson类,包含一个run的实例方法;VC中的代码如下:

    - (void)viewDidLoad {
        [super viewDidLoad];
        
        LGPerson *p = [[LGPerson alloc] init];
        
        [p run];
        
    }
    

    通过在终端输入clang -rewrite-objc ViewController.m -o new.cpp.m文件转 c++后看到如下东西:

    ...
    typedef struct objc_object LGPerson;
    typedef struct {} _objc_exc_LGPerson;
    #endif
    
    struct LGPerson_IMPL {
        struct NSObject_IMPL NSObject_IVARS; //    
    };
    ...
      LGPerson *p = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPerson"), sel_registerName("alloc")), sel_registerName("init"));
            ((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("run"));
    
    
    • 通过typedef struct objc_object LGPerson; 可以看到,原来我们的OC对象的本质是结构体
    • ·run·方法转成了objc_msgSend的消息可以看出:方法的本质是消息发送
    1. 什么是objc_msgSend?
      objc_msgSend是汇编写的一个C函数,用于消息的发送。
    2. 为什么用汇编去写?
      C写一个函数,没法保留未知的参数,跳转到任意的指针。汇编有寄存器可以做到;汇编快速,相比于C,快50%-80%;

    [p run];被转换成((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("run"));其中(id)p是消息接受者;sel_registerName("run")代表方法编号SEL _cmd,底层是一个字符串name;我们可以笼统的看成所有的方法都会转成类似于👇这样的一个实现(这里是没有带参数的🌰):

    void runIMP(id self,SEL _cmd){
        ...
    }
    

    通过sel,我们可以找到imp(函数实现的指针).怎么找到的???这个是重点(敲黑板ing),老司机源码解析告诉你。

    首先消息的发送((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("run"));前面代码就是做了类型的转换,我们可以写成如下:

    objc_msgSend(s,sel_registerName("run”));
    

    如果大家对sel_registerName感到陌生,可以执行如下代码打印看看就知道了:

    NSLog(@"%p---%p",sel_registerName("run"),@selector(run));
    //*************************************************************************
    2019-08-10 12:25:23.712418+0800 01---runtime[3542:307149] 0x10b64d4d3---0x10b64d4d3
    

    方法的本质是消息发送,在发送时,还隐式的传了两个参数消息接收者和方法编号;

    对象方法和类方法发送消息,向父类发送消息对比:

    1. 对象方法
    objc_msgSend(s,sel_registerName("run”));
    向父类发送消息:objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
    objc_super结构体代码如下:

    struct objc_super {
        /// Specifies an instance of a class.类的实例
        __unsafe_unretained _Nonnull id receiver;
    
        /// Specifies the particular superclass of the instance to message. 
    #if !defined(__cplusplus)  &&  !__OBJC2__
        /* For compatibility with old objc-runtime.h header */
        __unsafe_unretained _Nonnull Class class;
    #else
        __unsafe_unretained _Nonnull Class super_class;
    #endif
        /* super_class is the first class to search */
    };
        
    

    对应一个实例,就是这样的:

     struct objc_super mySuper;
        mySuper.receiver = p;
        mySuper.super_class = class_getSuperclass([p class]);
        objc_msgSendSuper(&mySuper,@selector(run))
    

    2. 类方法
    objc_msgSend(objc_getClass("LGPerson"),sel_registerName("run”));

    向父类发送消息:

         struct objc_super myClassSuper;
        myClassSuper.receiver = p;
        myClassSuper.super_class = class_getSuperclass(object_getClass([p class]));
        objc_msgSendSuper(&myClassSuper,sel_registerName(“run”))
    

    object_getClass([p class]) 是元类。元类就是类所属的类。

    面试题:

    对象方法存在哪? 对应的类里面.
    类方法存在哪? 类的元类里面,本质是一个实例方法.

    • 一个类方法,在元类里面,就是一个实例 ,所以类方法存在对应的元类里面,本质是一个实例方法。
    • OC的类其实也是一个对象,一个对象就要有一个它属于的类,意味着类也要有一个 isa 指针,指向他所属的类,也就是元类。

    当你给对象发送消息时,消息是在寻找这个对象的类的方法列表。
    当你给类发消息时,消息是在寻找这个类的元类的方法列表。

    Runtime源码分析

    源码下载地址:https://opensource.apple.com/ 最新的版本是objc4-750,大家也可以去GitHub上找有注释的源码,我在最后的demo下载中也会提供一份runtime的源码。

    objc_msgSend的消息的发送有两种方式:

      1. 快速的查找,从缓存找cache_t cache查找;

    缓存来源于我们的类.这个可以通过源码验证一下,在objc4的源码中找到class:

    struct objc_class : objc_object {
        // Class ISA;
        Class superclass;
    
        每一个类里面都会有一个cache,存储方法的sel,🙃
        imp,objc_msgSend首先回来这里通过sel查找imp,找到了,🙃
        直接返回,没有则下面介绍的第二种方式继续查找,找到了会存入cache。🙃
        cache_t cache;             
        class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    
        class_rw_t *data() { 
            return bits.data();
        }
        下面的代码不是重点介绍的,省略了🙃
        ...
    }
    
    
      1. 慢速的查找,lookup(C,C++,汇编查找);

    下面从objc_msgSend的源码入手,查看消息的发送流程:
    runtime源码中搜索_objc_msgSend(方法前面加_可以查看方法的汇编代码),可以看到有各种环境下的,这里以arm64为例子讲解:

    1目录
    objc_msgSend方法执行,首先进入到ENTRY
    Snip20190815_2.png
    LNilOrTagged:
        /*为空则直接返回  END_ENTRY _objc_msgSend*/
        b.eq    LReturnZero     // nil check
    
        // tagged
        mov x10, #0xf000000000000000
        cmp x0, x10
        b.hs    LExtTag
        adrp    x10, _objc_debug_taggedpointer_classes@PAGE
        add x10, x10, _objc_debug_taggedpointer_classes@PAGEOFF
        ubfx    x11, x0, #60, #4
        ldr x16, [x10, x11, LSL #3]
        //上面这些代码 是对isa进行了处理,具体不管它,LGetIsaDone这个才是重要方法🙃
        b   LGetIsaDone
    

    LNilOrTagged这个方法走完后,我们接着往下走,来到CacheLookup,就是从缓存中查找:

    CacheLookup NORMAL      // calls imp or objc_msgSend_uncached
    

    从注释可以看到,会出现两种情况: calls imp 也就是我们要的东西;或者执行objc_msgSend_uncached;下面是CacheLookup的实现

    /********************************************************************
     *
     * CacheLookup NORMAL|GETIMP|LOOKUP
     * 
     * Locate the implementation for a selector in a class method cache.
     *
     * Takes:
     *   x1 = selector
     *   x16 = class to be searched
     *
     * Kills:
     *   x9,x10,x11,x12, x17
     *
     * On exit: (found) calls or returns IMP
     *                  with x16 = class, x17 = IMP
     *          (not found) jumps to LCacheMiss
     *
     ********************************************************************/
    //.macro是一个宏的意思🙃
    .macro CacheLookup
        // x1 = SEL, x16 = isa
        ldp x10, x11, [x16, #CACHE] // x10 = buckets, x11 = occupied|mask
        and w12, w1, w11        // x12 = _cmd & mask
        add x12, x10, x12, LSL #4   // x12 = buckets + ((_cmd & mask)<<4)
    
        ldp x9, x17, [x12]      // {x9, x17} = *bucket
    1:  cmp x9, x1          // if (bucket->sel != _cmd)
        b.ne    2f          //     scan more
        CacheHit $0         // call or return imp
        
    2:  // not hit: x12 = not-hit bucket
        CheckMiss $0            // miss if bucket->sel == 0
        cmp x12, x10        // wrap if bucket == buckets
        b.eq    3f
        ldp x9, x17, [x12, #-16]!   // {x9, x17} = *--bucket
        b   1b          // loop
    
    3:  // wrap: x12 = first bucket, w11 = mask
        add x12, x12, w11, UXTW #4  // x12 = buckets+(mask<<4)
    

    注:一般我们看源码看到类似👆这样注释的基本都是核心代码,一个小技巧

    CacheLookup,先进入缓存进行查找CacheHit,这是一种快速查找的方法,通过哈希表找到对应的imp;如果没有找到--->CheckMiss,则进入慢速的查找lookup,慢速查找找到了也会进行缓存起来;add操作是其他地方找到了会调用,添加到缓存中。下面是没找到时执行的代码:

    .macro CheckMiss
        // miss if bucket->sel == 0
    .if $0 == GETIMP
        cbz x9, LGetImpMiss
    .elseif $0 == NORMAL
       //因为我们传入的参数 是NORMAL,所以走这里🙃
        cbz x9, __objc_msgSend_uncached
    .elseif $0 == LOOKUP
        cbz x9, __objc_msgLookup_uncached
    .else
    .abort oops
    .endif
    .endmacro
    

    因为我们传入的参数 是NORMAL,没找到则执行__objc_msgSend_uncached(.macro是宏定义)

    STATIC_ENTRY __objc_msgSend_uncached
        UNWIND __objc_msgSend_uncached, FrameWithNoSaves
    
        // THIS IS NOT A CALLABLE C FUNCTION
        // Out-of-band x16 is the class to search
        //里面就这一个方法,就他了,意思就是‘方法列表中查找’🙃
        MethodTableLookup
        br  x17
    
        END_ENTRY __objc_msgSend_uncached
    
    //*************************************************************
    .macro MethodTableLookup
        
        。。。
        // receiver and selector already in x0 and x1
        mov x2, x16
        bl  __class_lookupMethodAndLoadCache3
    
        // imp in x0
        mov x17, x0
        
        。。。
    
    .endmacro
    
    
    

    通过MethodTableLookup 方法列表查找imp; 会执行 __class_lookupMethodAndLoadCache3这个方法我们搜索时会发现怎么没有了,找不到了,很多小伙伴在这里直接懵逼了,其实这里需要从汇编回到C,干掉一个 _ 搜索_class_lookupMethodAndLoadCache3,找到如下代码:

    IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
    {        
        return lookUpImpOrForward(cls, sel, obj, 
                                  YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
    }
    
    

    YES/initialize/, 因为前面编译找到了isa,所以为YES
    NO/cache/, 因为没有缓存才来到这的,所以NO
    YES/resolver/ 最后都没有找到imp,则是否进行动态解析,所以YES

    👇代码的流程是这样的lookUpImpOrForward ------------> realizeClass(cls);---------> imp = cache_getImp(cls, sel); 重映射,再一次取,这是一个漫长的方法查找过程。代码如下,老长老长了,不想看可以跳过,看的话看我写的代码注释就可以了:

    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) {//这里肯定为NO🙃
            imp = cache_getImp(cls, sel);
            if (imp) return imp;
        }
      
        runtimeLock.read();
    
        if (!cls->isRealized()) {
            // Drop the read-lock and acquire the write-lock.
            // realizeClass() checks isRealized() again to prevent
            // a race while the lock is down.
            runtimeLock.unlockRead();
            runtimeLock.write();
           // 没有实现则走这个方法🙃
            realizeClass(cls);
    
            runtimeLock.unlockWrite();
            runtimeLock.read();
        }
    
        if (initialize  &&  !cls->isInitialized()) {
            runtimeLock.unlockRead();
            //初始化🙃
            _class_initialize (_class_getNonMetaClass(cls, inst));
            runtimeLock.read();
            // 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.assertReading();
    
        // Try this class's cache.
      这里再去一次缓存的原因是1.因为多线程异步执行等原因,🙃
      缓存中有了对应的imp;2.remap重映射的原因有了imp🙃
        imp = cache_getImp(cls, sel);
        if (imp) goto done;//找到了就结束🙃
    
        // Try this class's method lists.
        {//没找到继续getMethodNoSuper_nolock,传入class ,sel,具体方法实现我下面也给出来了🙃
            Method meth = getMethodNoSuper_nolock(cls, sel);
            if (meth) {
                log_and_fill_cache(cls, meth->imp, sel, inst, cls);//找到了,缓存起来🙃
                imp = meth->imp;
                goto done;//找到了,结果,回去。🙃
            }
        }
        //上面的流程没找到,开始找父类了 superclass🙃
        // Try superclass caches and method lists.
        {
            unsigned attempts = unreasonableClassCount();
            for (Class curClass = cls->superclass;
                 curClass != nil;//nil的加入是到了NSObject可以结束了,上面没有了🙃
                 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;
                    }
                }
                
               //再次调用getMethodNoSuper_nolock(curClass, sel);进行循环了进行循环了进行循环了,这是一个漫长的过程🙃
                // 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.unlockRead();
            _class_resolveMethod(cls, sel, inst);
            runtimeLock.read();
            // 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.unlockRead();
    
        return imp;
    }
    
    

    这里把上面过程中cache_getImp(cls, sel)没有找到后--->调用的getMethodNoSuper_nolock(cls, sel)展示给大家看一下:

    getMethodNoSuper_nolock(Class cls, SEL sel)
    {
        runtimeLock.assertLocked();
    
        assert(cls->isRealized());
        // fixme nil cls? 
        // fixme nil sel?
        //for 循环去方法列表中查找,从开始beginLists到结束endLists,把search_method_list查一遍🙃
        for (auto mlists = cls->data()->methods.beginLists(), 
                  end = cls->data()->methods.endLists(); 
             mlists != end;
             ++mlists)
        {
            method_t *m = search_method_list(*mlists, sel);
            if (m) return m;
        }
    
        return nil;
    }
    

    而如果还是没有找到,则执行动态方法解析,就是最后的那一小段代码,我单独再抠出来:

    动态方法解析

    //一直找不到,从这里开始开始动态方法解析了 ,尝试方法解析一次,是的,once🙃
    // No implementation found. Try method resolver once.
    
        if (resolver  &&  !triedResolver) {
           //这里只会进来一次,triedResolver = YES;🙃
            runtimeLock.unlockRead();
    //走这个方法🙃
            _class_resolveMethod(cls, sel, inst);
            runtimeLock.read();
            // 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.unlockRead();
    
        return imp;
    
    /***********************************************************************
    ✨✨✨Call +resolveClassMethod or +resolveInstanceMethod.✨✨✨
    **********************************************************************/
    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);
            }
        }
    }
    

    我们这里直接查看对象方法的实现_class_resolveInstanceMethod(cls, sel, inst);

    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;
        }
        //消息转发来了~~~ msg(cls, SEL_resolveInstanceMethod, sel);🙃
        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*/);
    
        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));
            }
        }
    }
    

    可以看到里面通过msg(cls, SEL_resolveInstanceMethod, sel);进行了消息的转发操作;通常消息转发和动态方法解析是互不相干的,在进入消息转发机制之前,respondsToSelector:instancesRespondToSelector:会被首先调用。您可以在这两个 方法中为传进来的选标提供一个IMP。如果您实现了resolveInstanceMethod:方法但是仍然希望正 常的消息转发机制进行,您只需要返回NO就可以了,否则消息转发流程不执行。

    OC中有两个方法可以调用:resolveClassMethodresolveInstanceMethod,既然有OC的方法,果断上demo尝试一下啦!(demo和源码都放在最后面了)其实就是借着一开始的那个例子,LGPerson内写一个类方法 +(void)walk;然后.mwalk的实现不写,我们用resolveClassMethod代替,如下:

    + (BOOL)resolveClassMethod:(SEL)sel{
        NSLog(@"来了 老弟");
        return  [super resolveClassMethod:sel];
    }
    
    //*******************************************
    2019-08-10 16:03:33.168525+0800 002---runtime动态解析[5727:409195] 来了 老弟
    2019-08-10 16:03:36.161490+0800 002---runtime动态解析[5727:409195] 来了 老弟
    

    会补救2次,还是没有,消息转发流程,最后抛出异常,崩溃...
    这里打个断点我们通过lldb打印一下看看:

    (lldb) bt
    * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
      * frame #0: 0x00000001056f660b 002---runtime动态解析`+[LGPerson resolveInstanceMethod:](self=LGPerson, _cmd="resolveInstanceMethod:", sel="run") at LGPerson.m:23:5
        frame #1: 0x0000000105fdae76 libobjc.A.dylib`resolveInstanceMethod(objc_class*, objc_selector*, objc_object*) + 91
        frame #2: 0x0000000105fd54e5 libobjc.A.dylib`lookUpImpOrForward + 464
        frame #3: 0x0000000105fe30d4 libobjc.A.dylib`_objc_msgSend_uncached + 68
    
    

    第二次执行

    2019-08-10 16:10:07.579418+0800 002---runtime动态解析[5786:412162] 来了 老弟
    (lldb) bt
    * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
      * frame #0: 0x00000001056f660b 002---runtime动态解析`+[LGPerson resolveInstanceMethod:](self=LGPerson, _cmd="resolveInstanceMethod:", sel="run") at LGPerson.m:23:5
        frame #1: 0x0000000105fdae76 libobjc.A.dylib`resolveInstanceMethod(objc_class*, objc_selector*, objc_object*) + 91
        frame #2: 0x0000000105fd54e5 libobjc.A.dylib`lookUpImpOrForward + 464
        frame #3: 0x0000000105fd529a libobjc.A.dylib`class_getInstanceMethod + 50
        frame #4: 0x0000000106a48dfb CoreFoundation`__methodDescriptionForSelector + 299
        frame #5: 0x0000000106a48ea6 CoreFoundation`-[NSObject(NSObject) methodSignatureForSelector:] + 38
        frame #6: 0x0000000106a2f1fa CoreFoundation`___forwarding___ + 378
        frame #7: 0x0000000106a31418 CoreFoundation`__forwarding_prep_0___ + 120
    
    

    看着打印是不是还是有点懵逼,没啥,👆的流程打印和图进行对照分析,就很清晰了,上图:(好尴尬,图不在这个电脑。。。后面补)


    消息转发流程.png

    消息转发

    动态方法解析失败后----->进入消息的转发:
    运行时系统会在抛出错误前,给该对象发送一条forwardInvocation:消息,该消息的唯一参数是个 NSInvocation 类型的对象——该对象封装了 原始的消息和消息的参数。您可以实现 forwardInvocation:方法来对不能处理的消息做一些默认的处理,也可以以其它的某种 方式来避免错误被抛出。如 forwardInvocation:的名字所示,它通常用来将消息转发给其它的对象。

    关于消息转发的作用,您可以考虑如下情景:假设,您需要设计一个能够响应 negotiate 消息的对象, 并且能够包括其它类型的对象对消息的响应。 通过在 negotiate 方法的实现中将 negotiate 消息 转发给其它的对象来很容易的达到这一目的。
    更进一步,假设您希望您的对象和另外一个类的对象对negotiate的消息的响应完全一致。一种可能的 方式就是让您的类继承其它类的方法实现。 然后,有时候这种方式不可行,因为您的类和其它类可能需要
    在不同的继承体系中响应 negotiate 消息。 虽然您的类无法继承其它类的negotiate方法,您仍然可以提供一个方法实现,这个方法实现只是简单
    的将 negotiate 消息转发给其他类的对象,就好像从其它类那儿“借”来的现一样。(消息的转发,后面直接来个demo实例讲解下就能很好理解了,上面的官方语言只是让我们知道消息转发能干嘛)。
    要转发消息给其它对象,forwardInvocation:方法所必须做的有:

    • 决定将消息转发给谁,并且
    • 将消息和原来的参数一块转发出去
      消息可以通过 invokeWithTarget:方法来转发.具体的转发流程操作,这里不再讲解,后面通过demo说明。

    以上就是通过方法的实质探查runtime的全部解析过程,这里大家再来看看思维导图对比一下,可能会清晰很多,两张图是两次总结Runtime画的,凑活看:


    runtime
    RunTime2

    分析Runtime时,有一个很重要的东西,叫isa,关于isa,我会在后面文章中介绍。
    最后,奉上demo下载地址(还没整理,后面补);
    最后的最后,送大家一句名言共勉:
    将来的你一定会感谢现在奋斗的自己。 ----李清照

    相关文章

      网友评论

          本文标题:Runtime底层原理之源码分析

          本文链接:https://www.haomeiwen.com/subject/uzgejctx.html