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

第八節(jié)課 消息流程分析之快速查找(下)

上篇文章我們通過源碼查看了方法底層調(diào)用的邏輯说莫,但是只分析到了objc_msgSend
的主體邏輯臼勉,并沒有深入了解折晦,那么這篇文章我們就繼續(xù)深入嵌纲。

深入前在抛,小小的回顧還是有必要的

主要的流程:
1.判斷當(dāng)前接收者是否存在cmp p0, #0

2.判斷是不是SUPPORT_TAGGED_POINTERS類型。如果是返吻,執(zhí)行b.le LNilOrTagged,然后在里面執(zhí)行 b.eq LReturnZero姑子。如果不是SUPPORT_TAGGED_POINTERS類型,直接b.eq LReturnZero思喊,此次objc_msgSend無效壁酬,結(jié)束本次消息發(fā)送。

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 //真機架構(gòu)
    ldr p11, [x16, #CACHE]  //x16進行平移#CACHE大小,存儲到p11璃搜。CACHE全局搜索賦值拖吼,是2 * __SIZEOF_POINTER__也就是2倍的指針大小,也就是16.平移后的位置就相當(dāng)于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就跳轉(zhuǎn)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 - 源碼調(diào)試 + 匯編
//  p11 cache -> p10 = buckets
//  p1(_cmd) & mask = index -> p12
//  (_cmd & mask) << 4  -> int 1 2 3 4 5   地址->int
//  buckets +  代表內(nèi)存平移 (1 2 3 4)
//  b[i] -> b + I
//  p13 當(dāng)前查找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字節(jié)(因為在objc_class中,cache正好距離首地址16字節(jié)香璃,即isa首地址 占8字節(jié)这难,superClass占8字節(jié)),獲取cache葡秒,cache中高16位存mask姻乓,低48位存buckets,即p11 = cache

【第二步】從cache中分別取出buckets和mask眯牧,并由mask根據(jù)哈希算法計算出哈希下標(biāo)
通過cache和掩碼(即0x0000fffffffffffe)的 & 運算蹋岩,將高16位mask抹零,得到buckets指針地址炸站,即p10 = buckets

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

objc_msgSend的參數(shù)p1(即第二個參數(shù)_cmd)& msak,通過哈希算法旱易,得到需要查找存儲sel-imp的bucket下標(biāo)index禁偎,即p12 = index = _cmd & mask,為什么通過這種方式呢?因為在存儲sel-imp時阀坏,也是通過同樣哈希算法計算哈希下標(biāo)進行存儲如暖,所以讀取也需要通過同樣的方式讀取。

總結(jié):

文字版流程

  1. 首先判斷recevier是否存在
  2. recevier -> isa -> class(p16)
  3. class通過內(nèi)存平移 -> cache(bucket mask)
  4. bucket掩碼 -> bucket
  5. mask掩碼 -> mask
  6. insert 哈希函數(shù) (mask_t)(value & mask)
  7. 得到第一次查找的index
  8. bucket + index bucket平移獲取整個緩存里面的第幾個
  9. bucket{imp sel}
  10. 拿到sel_cmd 進行比較 sel == _cmd -> cacheHit -> imp^isa = imp(br)調(diào)用imp
  11. sel != _cmd bucket 再次平移
  12. 死循環(huán) 遍歷
  13. 找不到 ->_objc_msgSend_uncached

圖片版流程


08-objmsgSend主流程.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末忌堂,一起剝皮案震驚了整個濱河市盒至,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌士修,老刑警劉巖枷遂,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異棋嘲,居然都是意外死亡酒唉,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門沸移,熙熙樓的掌柜王于貴愁眉苦臉地迎上來痪伦,“玉大人,你說我怎么就攤上這事雹锣⊥矗” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵蕊爵,是天一觀的道長辉哥。 經(jīng)常有香客問我,道長在辆,這世上最難降的妖魔是什么证薇? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任度苔,我火速辦了婚禮,結(jié)果婚禮上浑度,老公的妹妹穿的比我還像新娘寇窑。我一直安慰自己,他們只是感情好箩张,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布甩骏。 她就那樣靜靜地躺著,像睡著了一般先慷。 火紅的嫁衣襯著肌膚如雪饮笛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天论熙,我揣著相機與錄音福青,去河邊找鬼。 笑死脓诡,一個胖子當(dāng)著我的面吹牛无午,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播祝谚,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼宪迟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了交惯?” 一聲冷哼從身側(cè)響起次泽,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎席爽,沒想到半個月后意荤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡只锻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年袭异,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片炬藤。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖碴里,靈堂內(nèi)的尸體忽然破棺而出沈矿,到底是詐尸還是另有隱情,我是刑警寧澤咬腋,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布羹膳,位于F島的核電站,受9級特大地震影響根竿,放射性物質(zhì)發(fā)生泄漏陵像。R本人自食惡果不足惜就珠,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望醒颖。 院中可真熱鬧妻怎,春花似錦、人聲如沸泞歉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽腰耙。三九已至榛丢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間挺庞,已是汗流浹背晰赞。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留选侨,地道東北人掖鱼。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像侵俗,于是被迫代替她去往敵國和親锨用。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容