美文网首页
iOS底层原理 09 : objc_msgSend快速查找流程

iOS底层原理 09 : objc_msgSend快速查找流程

作者: smooth_lgh | 来源:发表于2020-09-21 18:11 被阅读0次

    1. Runtime

    Objective-C Runtime Programming Guide

    Runtime就是使用C,C++和汇编混合而成的,为OC提供运行时特性的一套机制。

    2.Runtime底层

    首先通过Clang编译main.c文件

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            LGPerson *person = [LGPerson alloc];
            [person sayNB];
            [person sayHello];
        }
        return 0;
    }
    

    在mian.cpp文件中,我们可以看到:
    1.OC层的方法调用会编译成objc_msgSend(消息接收者,sel_registerName("XXX"))
    2.所以@selector(XXX)等价于sel_registerName("XXX")
    3.[person sayNB]等价于objc_msgSend(person,sel_registerName("sayNB"))

             LGPerson *person = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPerson"), sel_registerName("alloc"));
             ((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayNB"));
             ((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayHello"));
    

    3.objc_msgSend的快速查找

    通过上一篇对cache_t的结构分析,我们知道(_sel,_imp)是存储在cache_t下的buckets的哈希表中

    cache_t内部结构图.png

    objc_msgSend会先从内存中查找,通过汇编快速查询到IMP,下面我们去源码里面看它的查找过程

    我们通过源码找到objc-msg-arm64.s文件里面

    // enter  开始进入 _objc_msgSend
    ENTRY _objc_msgSend  
        UNWIND _objc_msgSend, NoFrame
          // 判断消息接受者是否为空
        cmp p0, #0          // nil check and tagged pointer check
    #if SUPPORT_TAGGED_POINTERS
        b.le    LNilOrTagged        //  (MSB tagged pointer looks negative)
    #else
        b.eq    LReturnZero
    #endif
           // 获取isa
        ldr p13, [x0]       // p13 = isa
        GetClassFromIsa_p16 p13     // p16 = class
    LGetIsaDone:
        // calls imp or objc_msgSend_uncached
            //  从缓存里面获取imp
        CacheLookup NORMAL, _objc_msgSend
    
    #if SUPPORT_TAGGED_POINTERS
    // 小对象类型
    LNilOrTagged:
        b.eq    LReturnZero     // nil check
    
        // tagged
        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]
        adrp    x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGE
        add x10, x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGEOFF
        cmp x10, x16
        b.ne    LGetIsaDone
    
        // ext tagged
        adrp    x10, _objc_debug_taggedpointer_ext_classes@PAGE
        add x10, x10, _objc_debug_taggedpointer_ext_classes@PAGEOFF
        ubfx    x11, x0, #52, #8
        ldr x16, [x10, x11, LSL #3]
        b   LGetIsaDone
    // SUPPORT_TAGGED_POINTERS
    #endif
    
    LReturnZero:
        // x0 is already zero
        mov x1, #0
        movi    d0, #0
        movi    d1, #0
        movi    d2, #0
        movi    d3, #0
        ret
    
        END_ENTRY _objc_msgSend
    

    CacheLookup的实现:

    .macro CacheLookup
    LLookupStart$1:
    
        // p1 = SEL, p16 = isa
           // #define CACHE  
           // 所以p11指向了cache_t的首地址
        ldr p11, [x16, #CACHE]              // p11 = mask|buckets
    
    #if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    // p10 = p11 &  0x0000ffffffffffff
    // p10 指向cache_t下的buckets
        and p10, p11, #0x0000ffffffffffff   // p10 = buckets
        and p12, p1, p11, LSR #48       // x12 = _cmd & mask
    #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
        and p10, p11, #~0xf         // p10 = buckets
        and p11, p11, #0xf          // p11 = maskShift
        mov p12, #0xffff
        lsr p11, p12, p11               // p11 = mask = 0xffff >> p11
        and p12, p1, p11                // x12 = _cmd & mask
    #else
    #error Unsupported cache mask storage for ARM64.
    #endif
    
    // 内存平移
    // (1+PTRSHIFT):(1+3)
    // p12 = buckets + ((_cmd & mask) << (4))
    // 为什么左移4位, 因为bucket存了(SEL,IMP) ,恰好总共16字节。
    // 所以得到buckets[1]
        add p12, p10, p12, LSL #(1+PTRSHIFT)
                         // p12 = buckets + ((_cmd & mask) << (1+PTRSHIFT))
    
        ldp p17, p9, [x12]      // {imp, sel} = *bucket
    1:  cmp p9, p1          // if (bucket->sel != _cmd)
        b.ne    2f          //     scan more
        CacheHit $0         // call or return imp
        
    2:  // not hit: p12 = not-hit bucket
        CheckMiss $0            // miss if bucket->sel == 0
        cmp p12, p10        // wrap if bucket == buckets
        b.eq    3f
           // 先前查找
        ldp p17, p9, [x12, #-BUCKET_SIZE]!  // {imp, sel} = *--bucket
        b   1b          // loop
    
    3:  // wrap: p12 = first bucket, w11 = mask
    #if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
        add p12, p12, p11, LSR #(48 - (1+PTRSHIFT))
                        // p12 = buckets + (mask << 1+PTRSHIFT)
    #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
        add p12, p12, p11, LSL #(1+PTRSHIFT)
                        // p12 = buckets + (mask << 1+PTRSHIFT)
    #else
    #error Unsupported cache mask storage for ARM64.
    #endif
    
        // Clone scanning loop to miss instead of hang when cache is corrupt.
        // The slow path may detect any corruption and halt later.
    
        ldp p17, p9, [x12]      // {imp, sel} = *bucket
    1:  cmp p9, p1          // if (bucket->sel != _cmd)
        b.ne    2f          //     scan more
        CacheHit $0         // call or return imp
        
    2:  // not hit: p12 = not-hit bucket
        CheckMiss $0            // miss if bucket->sel == 0
        cmp p12, p10        // wrap if bucket == buckets
        b.eq    3f
        ldp p17, p9, [x12, #-BUCKET_SIZE]!  // {imp, sel} = *--bucket
        b   1b          // loop
    
    LLookupEnd$1:
    LLookupRecover$1:
    3:  // double wrap
        JumpMiss $0
    
    .endmacro
    

    大致过程:
    1.首先判断方法接受者p0存不存在.
    2.通过对象的isa获取class
    3.通过类的指针平移16字节,获取到cache.
    4.通过cache,通过计算得到buckets,mask以及index
    5.通过下标index获取buckets哈希表下标为index的值bucket_t
    6.将bucket.sel与_cmd对比,如果一致,则返回bucket.imp

    通过对汇编源码的分析,得到通过汇编在内存中查找IMP的流程

    objc_msgSend快速查找流程分析.png

    相关文章

      网友评论

          本文标题:iOS底层原理 09 : objc_msgSend快速查找流程

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