日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
Objc_MsgSend消息快速查找之Cache查找

本文轉(zhuǎn)載自微信公眾號(hào)「網(wǎng)羅開(kāi)發(fā)」,作者HotPotCat 。轉(zhuǎn)載本文請(qǐng)聯(lián)系網(wǎng)羅開(kāi)發(fā)公眾號(hào)。

巍山網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián),巍山網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為巍山上1000+提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站制作要多少錢(qián),請(qǐng)找那個(gè)售后服務(wù)好的巍山做網(wǎng)站的公司定做!

上一篇文章分析了 objc_msgSend 的匯編實(shí)現(xiàn),這邊文章繼續(xù)分析 objc_msgSend 中緩存的查找邏輯以及匯編代碼是如何進(jìn)入 c/c++ 代碼的。

1. CacheLookup 查找緩存

1.1 CacheLookup源碼分析

 
 
 
 
  1. //NORMAL, _objc_msgSend, __objc_msgSend_uncached
  2. .macro CacheLookup Mode, Function, MissLabelDynamic, MissLabelConstant
  3.     //   requirements:
  4.     //   //緩存不存在返回NULL,x0設(shè)置為0
  5.     //   GETIMP:
  6.     //     The cache-miss is just returning NULL (setting x0 to 0)
  7.     //   參數(shù)說(shuō)明
  8.     //   NORMAL and LOOKUP:
  9.     //   - x0 contains the receiver
  10.     //   - x1 contains the selector
  11.     //   - x16 contains the isa
  12.     //   - other registers are set as per calling conventions
  13.     //
  14. //調(diào)用過(guò)來(lái)的p16存儲(chǔ)的是cls,將cls存儲(chǔ)在x15.
  15.     mov x15, x16            // stash the original isa
  16. //_objc_msgSend
  17. LLookupStart\Function:
  18.     // p1 = SEL, p16 = isa
  19. //arm64 64 OSX/SIMULATOR
  20. #if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16_BIG_ADDRS
  21.     //isa->cache,首地址也就是_bucketsAndMaybeMask
  22.     ldr p10, [x16, #CACHE]              // p10 = mask|buckets
  23.     //lsr邏輯右移 p11 = _bucketsAndMaybeMask >> 48 也就是 mask
  24.     lsr p11, p10, #48           // p11 = mask
  25.     //p10 = _bucketsAndMaybeMask & 0xffffffffffff = buckets(保留后48位)
  26.     and p10, p10, #0xffffffffffff   // p10 = buckets
  27.     //x12 = cmd & mask   w1為第二個(gè)參數(shù)cmd(self,cmd...),w11也就是p11 也就是執(zhí)行cache_hash。這里沒(méi)有>>7位的操作
  28.     and w12, w1, w11            // x12 = _cmd & mask
  29. //arm64 64 真機(jī)這里p11計(jì)算后是_bucketsAndMaybeMask
  30. #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
  31.     ldr p11, [x16, #CACHE]          // p11 = mask|buckets
  32. //arm64 + iOS + !模擬器 + 非mac應(yīng)用
  33. #if CONFIG_USE_PREOPT_CACHES
  34. //iphone 12以后指針驗(yàn)證
  35. #if __has_feature(ptrauth_calls)
  36.     //tbnz 測(cè)試位不為0則跳轉(zhuǎn)。與tbz對(duì)應(yīng)。p11 第0位不為0則跳轉(zhuǎn) LLookupPreopt\Function。
  37.     tbnz    p11, #0, LLookupPreopt\Function
  38.     //p10 = _bucketsAndMaybeMask & 0x0000ffffffffffff = buckets
  39.     and p10, p11, #0x0000ffffffffffff   // p10 = buckets
  40. #else
  41.     //p10 = _bucketsAndMaybeMask & 0x0000fffffffffffe = buckets
  42.     and p10, p11, #0x0000fffffffffffe   // p10 = buckets
  43.     //p11 第0位不為0則跳轉(zhuǎn) LLookupPreopt\Function。
  44.     tbnz    p11, #0, LLookupPreopt\Function
  45. #endif
  46.     //eor 邏輯異或(^) 格式為:EOR{S}{cond} Rd, Rn, Operand2
  47.     //p12 = selector ^ (selector >> 7) select 右移7位&自己給到p12
  48.     eor p12, p1, p1, LSR #7
  49.     //p12 = p12 & (_bucketsAndMaybeMask >> 48) = index & mask值 = buckets中的下標(biāo)
  50.     and p12, p12, p11, LSR #48      // x12 = (_cmd ^ (_cmd >> 7)) & mask
  51. #else
  52.     //p10 = _bucketsAndMaybeMask & 0x0000ffffffffffff = buckets
  53.     and p10, p11, #0x0000ffffffffffff   // p10 = buckets
  54.     //p12 = selector & (_bucketsAndMaybeMask >>48) = sel & mask = buckets中的下標(biāo)
  55.     and p12, p1, p11, LSR #48       // x12 = _cmd & mask
  56. #endif // CONFIG_USE_PREOPT_CACHES
  57. //arm64 32
  58. #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
  59.     //后4位為mask前置0的個(gè)數(shù)的case
  60.     ldr p11, [x16, #CACHE]              // p11 = mask|buckets
  61.     and p10, p11, #~0xf         // p10 = buckets 相當(dāng)于后4位置為0,取前32位
  62.     and p11, p11, #0xf          // p11 = maskShift 取的是后4位,為mask前置位的0的個(gè)數(shù)
  63.     mov p12, #0xffff
  64.     lsr p11, p12, p11           // p11 = mask = 0xffff >> p11
  65.     and p12, p1, p11            // x12 = _cmd & mask
  66. #else
  67. #error Unsupported cache mask storage for ARM64.
  68. #endif
  69.     //通過(guò)上面的計(jì)算 p10 = buckets,p11 = mask(arm64真機(jī)是_bucketsAndMaybeMask), p12 = index
  70.     // p13(bucket_t) = buckets + 下標(biāo) << 4   PTRSHIFT arm64 為3.  <<4 位為16字節(jié) buckets + 下標(biāo) *16 = buckets + index *16 也就是直接平移到了第幾個(gè)元素的地址。
  71.     add p13, p10, p12, LSL #(1+PTRSHIFT)
  72.                         // p13 = buckets + ((_cmd & mask) << (1+PTRSHIFT))
  73.     //這里就直接遍歷查找了,因?yàn)閍rm64下cache_next相當(dāng)于遍歷(這里只掃描了前面)
  74.                         // do {
  75.     //p17 = imp, p9 = sel
  76. 1:  ldp p17, p9, [x13], #-BUCKET_SIZE   //     {imp, sel} = *bucket--
  77.     //sel - _cmd != 0 則跳轉(zhuǎn) 3:,也就意味著沒(méi)有找到就跳轉(zhuǎn)到__objc_msgSend_uncached
  78.     cmp p9, p1              //     if (sel != _cmd) {
  79.     b.ne    3f              //         scan more
  80.                         //     } else {
  81.     //找到則調(diào)用或者返回imp,Mode為 NORMAL
  82. 2:  CacheHit \Mode              // hit:    call or return imp  命中
  83.                         //     }
  84. //__objc_msgSend_uncached
  85. //緩存中找不到方法就走_(dá)_objc_msgSend_uncached邏輯了。
  86.     //cbz 為0跳轉(zhuǎn) sel == nil 跳轉(zhuǎn) \MissLabelDynamic
  87. 3:  cbz p9, \MissLabelDynamic       //     if (sel == 0) goto Miss; 有空位沒(méi)有找到說(shuō)明沒(méi)有緩存
  88.     //bucket_t - buckets 由于是遞減操作
  89.     cmp p13, p10            // } while (bucket >= buckets) //? 這里一直是往前找,后面的元素在后面還有一次循環(huán)。
  90.     //無(wú)符號(hào)大于等于 則跳轉(zhuǎn)1:f b 分別代表front與back
  91.     b.hs    1b
  92. //沒(méi)有命中cache  查找 p13 = mask對(duì)應(yīng)的元素,也就是倒數(shù)第二個(gè)
  93. #if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16_BIG_ADDRS
  94.     //p13 = buckets + (mask << 4) 平移找到對(duì)應(yīng)mask的bucket_t。UXTW 將w11擴(kuò)展為64位后左移4
  95.     add p13, p10, w11, UXTW #(1+PTRSHIFT)
  96.                         // p13 = buckets + (mask << 1+PTRSHIFT)
  97. #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
  98.     //p13 = buckets + (mask >> 44) 這里右移44位,少移動(dòng)4位就不用再左移了。因?yàn)閙askZeroBits的存在 就找到了mask對(duì)應(yīng)元素的地址
  99.     add p13, p10, p11, LSR #(48 - (1+PTRSHIFT))
  100.                         // p13 = buckets + (mask << 1+PTRSHIFT)
  101.                         // see comment about maskZeroBits
  102. #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
  103.     //p13 = buckets + (mask << 4) 找到對(duì)應(yīng)mask的bucket_t。
  104.     add p13, p10, p11, LSL #(1+PTRSHIFT)
  105.                         // p13 = buckets + (mask << 1+PTRSHIFT)
  106. #else
  107. #error Unsupported cache mask storage for ARM64.
  108. #endif
  109.     //p12 = buckets + (p12<<4) index對(duì)應(yīng)的bucket_t
  110.     add p12, p10, p12, LSL #(1+PTRSHIFT)
  111.                         // p12 = first probed bucket
  112.     //之前已經(jīng)往前查找過(guò)了,這里從后往index查找
  113.                         // do {
  114.     //p17 = imp p9 = sel
  115. 4:  ldp p17, p9, [x13], #-BUCKET_SIZE   //     {imp, sel} = *bucket--
  116.     //sel - _cmd
  117.     cmp p9, p1              //     if (sel == _cmd)
  118.     //sel == _cmd跳轉(zhuǎn)CacheHit
  119.     b.eq    2b              //         goto hit
  120.     //sel != nil
  121.     cmp p9, #0              // } while (sel != 0 &&
  122.     //
  123.     ccmp    p13, p12, #0, ne        //     bucket > first_probed)
  124.     //有值跳轉(zhuǎn)4:
  125.     b.hi    4b
  126. LLookupEnd\Function:
  127. LLookupRecover\Function:
  128. //仍然沒(méi)有找到緩存,緩存徹底不存在 __objc_msgSend_uncached()
  129.     b   \MissLabelDynamic

核心邏輯:

  • 根據(jù)不同架構(gòu)找到 buckets 中 sel 對(duì)應(yīng)的 index,p10 = buckets,p11 = mask / _bucketsAndMaybeMask(arm64_64 是 _bucketsAndMaybeMask),p12 = index。

arm64_64 的情況下如果 _bucketsAndMaybeMask 第 0 位為 1 則執(zhí)行 LLookupPreopt\Function。

  • p13 = buckets + index << 4 找到 cls 對(duì)應(yīng)的 buckets 地址,地址平移找到對(duì)應(yīng) bucket_t
  • do-while 循環(huán)掃描 buckets[index] 的前半部分(后半部分邏輯不在這里)。
    • 如果存在 sel 為空,則說(shuō)明是沒(méi)有緩存的,就直接 `__objc_msgSend_uncached()``。
    • 命中直接 CacheHit \Mode,這里 Mode 為 NORMAL。
  • 平移獲得 p13 = buckets[mask] 對(duì)應(yīng)的元素,也就是最后一個(gè)元素(arm64 下最后一個(gè)不存自身地址,也就相當(dāng)于 buckets[count - 1])。
  • p13 = buckets + mask << 4 找到 mask 對(duì)應(yīng)的 buckets 地址,地址平移找到對(duì)應(yīng) bucket_t
  • do-while 循環(huán)掃描 buckets[mask] 的前面元素,直到 index(不包含 index)。
    • 命中 CacheHit \Mode
    • 如果存在 sel 為空,則說(shuō)明是沒(méi)有緩存的,就直接結(jié)束循環(huán)。
  • 最終仍然沒(méi)有找到則執(zhí)行 __objc_msgSend_uncached()
  1. CACHE 是 cache_t 相對(duì) isa 的偏移。#define CACHE (2 * SIZEOF_POINTER)
  2. maskZeroBits 始終是 4 位 0,p13 = buckets + (_bucketsAndMaybeMask >> 44)右移 44 位后就不用再 <<4 找到對(duì)應(yīng) bucket_t 的地址了。這是因?yàn)?maskZeroBits 在 arm64_64 下存在的原因。
  3. f b 分別代表 front 與 back,往下往上的意思。

1.2 CacheLookup 偽代碼實(shí)現(xiàn)

 
 
 
 
  1. //NORMAL, _objc_msgSend, __objc_msgSend_uncached
  2. void CacheLookup(Mode,Function,MissLabelDynamic,MissLabelConstant) {
  3.     //1. 根據(jù)架構(gòu)不同集算sel在buckets中的index
  4.     if (arm64_64 && OSX/SIMULATOR) {
  5.         p10 = isa->cache //_bucketsAndMaybeMask
  6.         p11 = _bucketsAndMaybeMask >> 48//mask
  7.         p10 = _bucketsAndMaybeMask & 0xffffffffffff//buckets
  8.         x12 = sel & mask //index 也就是執(zhí)行cache_hash
  9.     } else if (arm64_64) {//真機(jī) //這個(gè)分支下沒(méi)有計(jì)算mask
  10.         p11 = isa->cache //_bucketsAndMaybeMask
  11.         if (arm64 + iOS + !模擬器 + 非mac應(yīng)用) {
  12.             if (開(kāi)啟指針驗(yàn)證 ) {
  13.                 if (_bucketsAndMaybeMask 第0位 != 0) {
  14.                     goto LLookupPreopt\Function
  15.                 } else {
  16.                     p10 = _bucketsAndMaybeMask & 0x0000ffffffffffff//buckets
  17.                 }
  18.             } else {
  19.                 p10 = _bucketsAndMaybeMask & 0x0000fffffffffffe //buckets
  20.                 if (_bucketsAndMaybeMask 第0位 != 0) {
  21.                     goto LLookupPreopt\Function
  22.                 }
  23.             }
  24.             //計(jì)算index
  25.             p12 = selector ^ (selector >> 7)
  26.             p12 = p12 & (_bucketsAndMaybeMask & 48) = p12 & mask//index
  27.         } else {
  28.             p10 = _bucketsAndMaybeMask & 0x0000ffffffffffff //buckets
  29.             p12 = selector & (_bucketsAndMaybeMask >>48) //index
  30.         }
  31.     } else if (arm64_32) {
  32.         p11 = _bucketsAndMaybeMask
  33.         p10 =  _bucketsAndMaybeMask &(~0xf)//buckets 相當(dāng)于后4位置為0,取前32位
  34.         p11 = _bucketsAndMaybeMask & 0xf //mask前置位0的個(gè)數(shù)
  35.         p11 =  0xffff >> p11 //獲取到mask的值
  36.         x12 = selector & mask //index
  37.     } else {
  38.         #error Unsupported cache mask storage for ARM64.
  39.     }
  40.     
  41.     //通過(guò)上面的計(jì)算 p10 = buckets,p11 = mask/_bucketsAndMaybeMask, p12 = index
  42.     p13 = buckets + index << 4 //找到cls對(duì)應(yīng)的buckets地址。地址平移找到對(duì)應(yīng)bucket_t。
  43.     
  44.     //2.找緩存(這里只掃描了前面)
  45.     do {
  46.         p13 = *bucket-- //賦值后指向前一個(gè)bucket
  47.         p17 = bucket.imp
  48.         p9 = bucket.sel
  49.         if (p9 != selector) {
  50.             if (p9 == 0) {//說(shuō)明沒(méi)有緩存
  51.                 __objc_msgSend_uncached()
  52.             }
  53.         } else {//緩存命中,走命中邏輯 call or return imp
  54.             CacheHit \Mode
  55.         }
  56.     } while(bucket >= buckets) //buckets是首地址,bucket是index對(duì)應(yīng)的buckct往前移動(dòng)
  57.   
  58.     //查找完后還沒(méi)有緩存?
  59.     //查找 p13 = mask對(duì)應(yīng)的元素,也就是最后一個(gè)元素
  60.     if (arm64_64 && OSX/SIMULATOR) {
  61.         p13 = buckets + (mask << 4)
  62.     } else if (arm64_64) {//真機(jī)
  63.         p13 = buckets + (_bucketsAndMaybeMask >> 44)//這里右移44位,少移動(dòng)4位就不用再左移了。這里就找到了對(duì)應(yīng)index的bucket_t。
  64.     } else if (arm64_32) {
  65.         p13 = buckets + (mask << 4)
  66.     } else {
  67.         #error Unsupported cache mask storage for ARM64.
  68.     }
  69.     
  70.     //index的bucket_t 從mask對(duì)應(yīng)的buckets開(kāi)始再往前找
  71.     p12 = buckets + (index<<4)
  72.     do {
  73.         p17 = imp;
  74.         p9 = sel;
  75.         *p13--;
  76.         if (p9 == selector) {//命中
  77.             CacheHit \Mode
  78.         }
  79.     } while (p9 != nil && bucket > p12)//從后往前 p9位nil則證明沒(méi)有存,也就不存在緩存了。
  80.     
  81.     //仍然沒(méi)有找到緩存,緩存徹底不存在。
  82.     __objc_msgSend_uncached()
  83. }

2. LLookupPreopt\Function

在 arm64_64 真機(jī)的情況下,如果 _bucketsAndMaybeMask 的第 0 位為 1 則會(huì)執(zhí)行 LLookupPreopt\Function 的邏輯。簡(jiǎn)單看了下匯編發(fā)現(xiàn)與 cache_t 中的 _originalPreoptCache 有關(guān)。

2.1 LLookupPreopt\Function 源碼分析

 
 
 
 
  1. LLookupPreopt\Function:
  2. #if __has_feature(ptrauth_calls)
  3.     //p10 = _bucketsAndMaybeMask & 0x007ffffffffffffe = buckets
  4.     and p10, p11, #0x007ffffffffffffe   // p10 = x
  5.     //buckets x16為cls 驗(yàn)證
  6.     autdb   x10, x16            // auth as early as possible
  7. #endif
  8.     // x12 = (_cmd - first_shared_cache_sel)
  9.     //(_cmd >> 12 + PAGE) << 12 + PAGEOFF 第一個(gè)sel
  10.     adrp    x9, _MagicSelRef@PAGE
  11.     ldr p9, [x9, _MagicSelRef@PAGEOFF]
  12.     //差值index
  13.     sub p12, p1, p9
  14.     // w9  = ((_cmd - first_shared_cache_sel) >> hash_shift & hash_mask)
  15. #if __has_feature(ptrauth_calls)
  16.     // bits 63..60 of x11 are the number of bits in hash_mask
  17.     // bits 59..55 of x11 is hash_shift
  18.     // 取到 hash_shift...
  19.     lsr x17, x11, #55           // w17 = (hash_shift, ...)
  20.     //w9 = index >> hash_shift
  21.     lsr w9, w12, w17            // >>= shift
  22.     //x17 = _bucketsAndMaybeMask >>60 //mask_bits
  23.     lsr x17, x11, #60           // w17 = mask_bits
  24.     mov x11, #0x7fff
  25.     //x11 = 0x7fff >> mask_bits //mask
  26.     lsr x11, x11, x17           // p11 = mask (0x7fff >> mask_bits)
  27.     //x9 = x9 & mask
  28.     and x9, x9, x11         // &= mask
  29. #else
  30.     // bits 63..53 of x11 is hash_mask
  31.     // bits 52..48 of x11 is hash_shift
  32.     lsr x17, x11, #48           // w17 = (hash_shift, hash_mask)
  33.     lsr w9, w12, w17            // >>= shift
  34.     and x9, x9, x11, LSR #53        // &=  mask
  35. #endif
  36.     //x17 = el_offs | (imp_offs << 32)
  37.     ldr x17, [x10, x9, LSL #3]      // x17 == sel_offs | (imp_offs << 32)
  38.     // cmp x12  x17 是否找到sel
  39.     cmp x12, w17, uxtw
  40. .if \Mode == GETIMP
  41.     b.ne    \MissLabelConstant      // cache miss
  42.     //imp = isa - (sel_offs >> 32)
  43.     sub x0, x16, x17, LSR #32       // imp = isa - imp_offs
  44.     //注冊(cè)imp
  45.     SignAsImp x0
  46.     ret
  47. .else
  48.     b.ne    5f              // cache miss
  49.     //imp(x17) =  (isa - sel_offs>> 32)
  50.     sub x17, x16, x17, LSR #32      // imp = isa - imp_offs
  51. .if \Mode == NORMAL
  52.     //跳轉(zhuǎn)imp
  53.     br  x17
  54. .elseif \Mode == LOOKUP
  55.     //x16 = isa | 3 //這里為或的意思
  56.     orr x16, x16, #3 // for instrumentation, note that we hit a constant cache
  57.     //注冊(cè)imp
  58.     SignAsImp x17
  59.     ret
  60. .else
  61. .abort  unhandled mode \Mode
  62. .endif
  63.     //x9 = buckets-1
  64. 5:  ldursw  x9, [x10, #-8]          // offset -8 is the fallback offset
  65.     //計(jì)算回調(diào)isa  x16 = x16 + x9
  66.     add x16, x16, x9            // compute the fallback isa
  67.     //使用新isa重新查找緩存
  68.     b   LLookupStart\Function       // lookup again with a new isa
  69. .endif
  • 找到 imp 就跳轉(zhuǎn)/返回。
  • 沒(méi)有找到返回下一個(gè) isa 重新 CacheLookup。
  • 這塊進(jìn)入的查找共享緩存, 與 cache_t 的 _originalPreoptCache 有關(guān)。maskZeroBits 這 4 位就是用來(lái)判斷是否有 _originalPreoptCache 的。

@TODO 真機(jī)調(diào)試的時(shí)候進(jìn)不到這塊流程,這塊分析的還不是很透徹,后面再補(bǔ)充。

3. CacheHit

在查找緩存命中后會(huì)執(zhí)行 CacheHit。

3.1 CacheHit源碼分析

 
 
 
 
  1. #define NORMAL 0
  2. #define GETIMP 1
  3. #define LOOKUP 2
  4. // CacheHit: x17 = cached IMP, x10 = address of buckets, x1 = SEL, x16 = isa
  5. .macro CacheHit
  6. //這里傳入的為NORMAL
  7. .if $0 == NORMAL
  8.     //調(diào)用imp TailCallCachedImp(imp,buckets,sel,isa)
  9.     TailCallCachedImp x17, x10, x1, x16 // authenticate and call imp
  10. .elseif $0 == GETIMP
  11.     //返回imp
  12.     mov p0, p17
  13.     //imp == nil跳轉(zhuǎn)9:
  14.     cbz p0, 9f          // don't ptrauth a nil imp
  15.     //有imp執(zhí)行AuthAndResignAsIMP(imp,buckets,sel,isa)最后給到x0返回。
  16.     AuthAndResignAsIMP x0, x10, x1, x16 // authenticate imp and re-sign as IMP
  17. 9:  ret             // return IMP
  18. .elseif $0 == LOOKUP
  19.     // No nil check for ptrauth: the caller would crash anyway when they
  20.     // jump to a nil IMP. We don't care if that jump also fails ptrauth.
  21.     //找imp(imp,buckets,sel,isa)
  22.     AuthAndResignAsIMP x17, x10, x1, x16    // authenticate imp and re-sign as IMP
  23.     //isa與x15比較
  24.     cmp x16, x15
  25.     //cinc如果相等 就將x16+1,否則就設(shè)成0.
  26.     cinc    x16, x16, ne            // x16 += 1 when x15 != x16 (for instrumentation ; fallback to the parent class)
  27.     ret             // return imp via x17
  28. .else
  29. .abort oops
  30. .endif
  31. .endmacro
  • 這里其實(shí)走的是 NORMAL 邏輯,NORMAL 的 case 直接驗(yàn)證并且跳轉(zhuǎn) imp。
  • TailCallCachedImp 內(nèi)部執(zhí)行的是 imp^cls,對(duì) imp 進(jìn)行了解碼。
  • GETIMP 返回 imp。
  • LOOKUP 查找注冊(cè) imp 并返回。

3.1 CacheHit 偽代碼實(shí)現(xiàn)

 
 
 
 
  1. //x17 = cached IMP, x10 = address of buckets, x1 = SEL, x16 = isa
  2. void CacheHit(Mode) {
  3.     if (Mode == NORMAL) {
  4.         //imp = imp^cls 解碼
  5.         TailCallCachedImp x17, x10, x1, x16    // 解碼跳轉(zhuǎn)imp
  6.     } else if (Mode == GETIMP) {
  7.         p0 = IMP
  8.         if (p0 == nil) {
  9.             return
  10.         } else {
  11.             AuthAndResignAsIMP(imp,buckets,sel,isa)//resign cached imp as IMP
  12.         }
  13.     } else if (Mode == LOOKUP) {
  14.         AuthAndResignAsIMP(x17, buckets, sel, isa)//resign cached imp as IMP
  15.         if (isa == x15) {
  16.             x16 += 1
  17.         } else {
  18.             x16 = 0
  19.         }
  20.     } else {
  21.         .abort oops//報(bào)錯(cuò)
  22.     }
  23. }

4. __objc_msgSend_uncached

在緩存沒(méi)有命中的情況下會(huì)走到 __objc_msgSend_uncached() 的邏輯:

 
 
 
 
  1. STATIC_ENTRY __objc_msgSend_uncached
  2. UNWIND __objc_msgSend_uncached, FrameWithNoSaves
  3. // THIS IS NOT A CALLABLE C FUNCTION
  4. // Out-of-band p15 is the class to search
  5. //查找imp
  6. MethodTableLookup
  7. //跳轉(zhuǎn)imp
  8. TailCallFunctionPointer x17
  9. END_ENTRY __objc_msgSend_uncached
  • MethodTableLookup 查找 imp
  • TailCallFunctionPointer 跳轉(zhuǎn) imp
 
 
 
 
  1. .macro MethodTableLookup
  2.     
  3.     SAVE_REGS MSGSEND
  4.     // lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
  5.     // receiver and selector already in x0 and x1
  6.     //x2 = cls
  7.     mov x2, x16
  8.     //x3 = LOOKUP_INITIALIZE|LOOKUP_RESOLVER //是否初始化,imp沒(méi)有實(shí)現(xiàn)嘗試resolver
  9. //_lookUpImpOrForward(receiver,selector,cls,LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
  10.     mov x3, #3
  11.     bl  _lookUpImpOrForward
  12.     // IMP in x0
  13.     mov x17, x0
  14.     RESTORE_REGS MSGSEND
  15. .endmacro
  • 調(diào)用 _lookUpImpOrForward 查找 imp。這里就調(diào)用到了 c/c++ 的代碼了:
 
 
 
 
  1. IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)

最終會(huì)調(diào)用 _lookUpImpOrForward 進(jìn)入 c/c++ 環(huán)境邏輯。

對(duì)于架構(gòu)的一些理解:

  • LP64 //64位
  • x86_64 // interl 64位
  • i386 // intel 32位
  • arm // arm指令 32 位
  • arm64 //arm64指令
  • arm64 && LP64 //arm64 64位
  • arm64 && !LP64 //arm64 32 位

當(dāng)然也可以通過(guò)真機(jī)跟蹤匯編代碼讀取寄存器進(jìn)行,與源碼分析的是一致的,走其中的一個(gè)分支。

5. objc_msgSend流程圖

總結(jié)

  • 判斷 receiver 是否存在。
  • 通過(guò) isa 獲取 cls。
  • cls 內(nèi)存平移 0x10 獲取 cache 也就是 _bucketsAndMaybeMask。
  • 通過(guò) buckets & bucketsMask 獲取 buckets地址。
  • 通過(guò) bucketsMask >> maskShift 獲取 mask。
  • 通過(guò) sel & mask 獲取第一次查找的 index。
  • buckets + index << 4 找到 index 對(duì)應(yīng)的地址。
  • do-while 循環(huán)判斷找緩存,這次從 [index~0] 查找 imp。
  • 取到 buckets[mask] 繼續(xù) do-while 循環(huán),從 [mask~index) 查找 imp。兩次查找過(guò)程中如果有 sel 為空則會(huì)結(jié)束查找。走 __objc_msgSend_uncached 的邏輯。
  • 找到 imp 就解碼跳轉(zhuǎn) imp。

文章標(biāo)題:Objc_MsgSend消息快速查找之Cache查找
當(dāng)前地址:http://www.dlmjj.cn/article/djejoid.html