iOS底层原理_08消息流程分析之快速查找(下)

第八节课 消息流程分析之快速查找(下)

上篇文章我们通过源码查看了方法底层调用的逻辑,但是只分析到了objc_msgSend
的主体逻辑,并没有深入了解,那么这篇文章我们就继续深入。

深入前,小小的回顾还是有必要的

主要的流程:
1.判断当前接收者是否存在cmp p0, #0

2.判断是不是SUPPORT_TAGGED_POINTERS类型。如果是,执行b.le LNilOrTagged,然后在里面执行 b.eq LReturnZero。如果不是SUPPORT_TAGGED_POINTERS类型,直接b.eq LReturnZero,此次objc_msgSend无效,结束本次消息发送。

3.如果p0存在,则将x0存入到p13,x0receiver,即类,即类的首地址,即isa,也就是说p13=isa。通过isa拿到class

GetClassFromIsa_p16 p13, 1, x0

4.查看GetClassFromIsa_p16源码,最终获取到p16=class

5.CacheLookup NORMAL

这篇文章我们就主要来分析这个CacheLookup NORMAL

CacheLookup 缓存查找汇编源码

.macro CacheLookup 
    //
    // Restart protocol:
    //
    //   As soon as we're past the LLookupStart$1 label we may have loaded
    //   an invalid cache pointer or mask.
    //
    //   When task_restartable_ranges_synchronize() is called,
    //   (or when a signal hits us) before we're past LLookupEnd$1,
    //   then our PC will be reset to LLookupRecover$1 which forcefully
    //   jumps to the cache-miss codepath which have the following
    //   requirements:
    //
    //   GETIMP:
    //     The cache-miss is just returning NULL (setting x0 to 0)
    //
    //   NORMAL and LOOKUP:
    //   - x0 contains the receiver
    //   - x1 contains the selector
    //   - x16 contains the isa
    //   - other registers are set as per calling conventions
    //
    
    mov x15, x16            // stash the original isa
LLookupStart\Function:
    // p1 = SEL, p16 = isa
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16_BIG_ADDRS
    ldr p10, [x16, #CACHE]              // p10 = mask|buckets
    lsr p11, p10, #48           // p11 = mask
    and p10, p10, #0xffffffffffff   // p10 = buckets
    and w12, w1, w11            // x12 = _cmd & mask
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16 //真机架构
    ldr p11, [x16, #CACHE]  //x16进行平移#CACHE大小,存储到p11。CACHE全局搜索赋值,是2 * __SIZEOF_POINTER__也就是2倍的指针大小,也就是16.平移后的位置就相当于cache_t的位置。p11 = cache_t
            // p11 = mask|buckets
    #if CONFIG_USE_PREOPT_CACHES
        #if __has_feature(ptrauth_calls)
    tbnz    p11, #0, LLookupPreopt\Function
    and p10, p11, #0x0000ffffffffffff   // p10 = buckets
        #else
    and p10, p11, #0x0000fffffffffffe   // p10 = buckets
    tbnz    p11, #0, LLookupPreopt\Function   //不为0就跳转LLookupPreopt
        #endif
    eor p12, p1, p1, LSR #7 
    and p12, p12, p11, LSR #48  //cache_t(_bucketsAndMaybeMask)右移48位,即清空了buckets,可以得到mask,最后将p12 & mask,得到了第一次查找bucket_t的index,即first_probed
        // x12 = (_cmd ^ (_cmd >> 7)) & mask
    #else

//  p11 cache -> p10 = buckets
//  p11, LSR #48 -> mask
//  p1(_cmd) & mask = index -> p12
    and p10, p11, #0x0000ffffffffffff   // p10 = buckets
    and p12, p1, p11, LSR #48       // x12 = _cmd & mask

#endif // CONFIG_USE_PREOPT_CACHES
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
    ldr p11, [x16, #CACHE]              // p11 = mask|buckets
    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


// objc - 源码调试 + 汇编
//  p11 cache -> p10 = buckets
//  p1(_cmd) & mask = index -> p12
//  (_cmd & mask) << 4  -> int 1 2 3 4 5   地址->int
//  buckets +  代表内存平移 (1 2 3 4)
//  b[i] -> b + I
//  p13 当前查找bucket
    add p13, p10, p12, LSL #(1+PTRSHIFT)
                        // p13 = buckets + ((_cmd & mask) << (1+PTRSHIFT))

                        // do {
//  *bucket--  p17, p9
//  bucket 里面的东西 imp (p17) sel (p9)
//  查到的 sel (p9) 和我们 say1
1:  ldp p17, p9, [x13], #-BUCKET_SIZE   //     {imp, sel} = *bucket--
    cmp p9, p1              //     if (sel != _cmd) {
    b.ne    3f              //         scan more
                        //     } else {
2:  CacheHit \Mode              // hit:    call or return imp
                        //     }
3:  cbz p9, \MissLabelDynamic       //     if (sel == 0) goto Miss;
    cmp p13, p10            // } while (bucket >= buckets)
    b.hs    1b

主要分为以下几步:
【第一步】通过cache首地址平移16字节(因为在objc_class中,cache正好距离首地址16字节,即isa首地址 占8字节,superClass占8字节),获取cache,cache中高16位存mask,低48位存buckets,即p11 = cache

【第二步】从cache中分别取出buckets和mask,并由mask根据哈希算法计算出哈希下标
通过cache和掩码(即0x0000fffffffffffe)的 & 运算,将高16位mask抹零,得到buckets指针地址,即p10 = buckets

cache右移48位,得到mask,即p11 = mask

objc_msgSend的参数p1(即第二个参数_cmd)& msak,通过哈希算法,得到需要查找存储sel-imp的bucket下标index,即p12 = index = _cmd & mask,为什么通过这种方式呢?因为在存储sel-imp时,也是通过同样哈希算法计算哈希下标进行存储,所以读取也需要通过同样的方式读取。

总结:

文字版流程

  1. 首先判断recevier是否存在
  2. recevier -> isa -> class(p16)
  3. class通过内存平移 -> cache(bucket mask)
  4. bucket掩码 -> bucket
  5. mask掩码 -> mask
  6. insert 哈希函数 (mask_t)(value & mask)
  7. 得到第一次查找的index
  8. bucket + index bucket平移获取整个缓存里面的第几个
  9. bucket{imp sel}
  10. 拿到sel_cmd 进行比较 sel == _cmd -> cacheHit -> imp^isa = imp(br)调用imp
  11. sel != _cmd bucket 再次平移
  12. 死循环 遍历
  13. 找不到 ->_objc_msgSend_uncached

图片版流程


08-objmsgSend主流程.png

你可能感兴趣的:(iOS底层原理_08消息流程分析之快速查找(下))