初探OC底层原理之objc_msgSend(下)

方法本质和objc_msgSend 汇编源码分析

  • objc_msgSend(recevier, _cmd)
  • 1:判断 recevier(消息接收者) 是否存在
  • recevier -> class ->isa
    CacheLookup LOOKUP, _objc_msgLookup, __objc_msgLookup_uncached

 // NORMAL, _objc_msgSend, __objc_msgSend_uncached ,  MissLabelConstant
.macro CacheLookup Mode, Function, MissLabelDynamic, MissLabelConstant
    //
    // Restart protocol:
    //
    //   As soon as we're past the LLookupStart\Function 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\Function,
    //   then our PC will be reset to LLookupRecover\Function 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]          // 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
        #endif
    eor p12, p1, p1, LSR #7
    and p12, p12, p11, LSR #48      // 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
  • ldr p11, [x16, #CACHE] 找到CACHE定义 #define CACHE (2 * SIZEOF_POINTER) 得出CACHE是16, x16是class 平移16 得到cache_t,所以 P11 = cache_t
  • and p10, p11, #0x0000fffffffffffe tbnz p11, #0, LLookupPreopt\Function 这两句得出cache_t&0x0000fffffffffffe(mask)的操作 对应如下图


    image.png

得到 bucket_t 所有P10 = bucket_t
eor p12, p1, p1, LSR #7
and p12, p12, p11, LSR #48 // x12 = (_cmd ^ (_cmd >> 7)) & mask 这2句得出如


image.png

找到cache_hash的 index 进行递归缓存命中查找 如下

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

总结: objc_msgSend 在底层汇编就是快速(cmd -> imp)的过程,通过找到缓存池里的buckets 然后找到对应的index 进行地址平移查找,如果一直都找不到,直接跳转至CheckMiss,因为$0是normal,会跳转至__objc_msgSend_uncached,即进入慢速查找流程


image.png

你可能感兴趣的:(初探OC底层原理之objc_msgSend(下))