OC對(duì)象的方法緩存

1梅尤、回顧

在程序運(yùn)行的時(shí)候柜思,oc對(duì)象在內(nèi)存中的存儲(chǔ)結(jié)構(gòu)是objc_class類(lèi)型的,objc_class存放著類(lèi)的方法列表巷燥,屬性列表赡盘,協(xié)議列表,成員變量列表
還存放著方法的緩存列表

2缰揪、方法緩存列表

思考:為什么要?jiǎng)?chuàng)建方法緩存列表陨享,目的是什么

2.1 方法的調(diào)用

假如調(diào)用對(duì)象方法的時(shí)候,首先從對(duì)象的方法列表中查詢(xún)方法钝腺,如果不存在抛姑,通過(guò)superclass查找父類(lèi)的方法列表,直到找到方法艳狐。


image.png

按照這樣的邏輯執(zhí)行的話(huà)定硝,每次調(diào)用方法都會(huì)去查詢(xún)方法,這樣會(huì)造成資源的浪費(fèi)

加入存在一個(gè)方法的緩存列表毫目,第一次調(diào)用方法的時(shí)候蔬啡,都將這個(gè)方法緩存起來(lái),那么下次再調(diào)用這個(gè)方法的時(shí)候镀虐,就可以直接從緩存列表中讀取箱蟆,不需要再按照流程去查詢(xún)方法

image.png

再次調(diào)用使用過(guò)的方法的時(shí)候,直接從緩存列表中讀取

3粉私、緩存列表的內(nèi)部結(jié)構(gòu) cache_t

// cache_t 內(nèi)部主要有三個(gè)屬性
struct cache_t {
    struct bucket_t *_buckets; // 是一個(gè)數(shù)組 散列表
    mask_t _mask; // 散列表的長(zhǎng)度 - 1顽腾, 散列表的長(zhǎng)度 >= 已經(jīng)緩存的數(shù)量
    mask_t _occupied; // 已經(jīng)緩存的方法數(shù)量
}

struct bucket_t {
private:
#if __arm64__ // 手機(jī)
     SEL _sel; // SEL作為key
    uintptr_t _imp;  // 函數(shù)的地址作為value
#else
    SEL _sel;  // SEL作為key
    uintptr_t _imp; // 函數(shù)的地址作為value
#endif
}
public:
// 獲取sel
    inline SEL sel() const { return _sel; }
// 獲取imp
    inline IMP imp() const {
        if (!_imp) return nil;
        return (IMP)
            ptrauth_auth_and_resign((const void *)_imp,
                                    ptrauth_key_process_dependent_code,
                                    modifierForSEL(_sel),
                                    ptrauth_key_function_pointer, 0);
    }

    template <Atomicity>
// 賦值 sel作為key imp作為value
    void set(SEL newSel, IMP newImp);
};

4、存儲(chǔ)與查詢(xún)方式

/// 查詢(xún)方法的方式
//通過(guò)sel 方法名
bucket_t * cache_t::find(SEL s, id receiver)
{
    assert(s != 0);
   // 1.獲取散列表
    bucket_t *b = buckets();
 // 2.獲取_mask 散列表的長(zhǎng)度 - 1
    mask_t m = mask();
// 3.方法名作為key ,cache_hash 內(nèi)部是將sel & mask 獲取位置下標(biāo)
    mask_t begin = cache_hash(s, m);
// 4. i 存放的位置
    mask_t i = begin;
    do {
       ///如果 為空 或方法相同抄肖,返回
        if (b[i].sel() == 0  ||  b[i].sel() == s) {
            return &b[I];
        }
        // 如果方法不一致久信,將i -1 向上一個(gè)位置查詢(xún),直到返回結(jié)果
    } while ((i = cache_next(i, m)) != begin);

    // hack
    Class cls = (Class)((uintptr_t)this - offsetof(objc_class, cache));
    cache_t::bad_cache(receiver, (SEL)s, cls);
}

通過(guò)源碼分析發(fā)現(xiàn)漓摩,蘋(píng)果不是單純的通過(guò)遍歷的方式查詢(xún)方法的位置裙士。 而是通過(guò)將 sel 與 mask進(jìn)行 &預(yù)算計(jì)算出 方法存放的下標(biāo),如果下標(biāo)內(nèi)已經(jīng)存放了方法管毙,會(huì)繼續(xù)往上一個(gè)位置存放

如果散列表中分配的內(nèi)存已經(jīng)存滿(mǎn)腿椎,會(huì)重新分配
void cache_t::expand()
{
    cacheUpdateLock.assertLocked();
   
    // 舊的數(shù)量
    uint32_t oldCapacity = capacity();
// 新的數(shù)量 擴(kuò)容2倍
    uint32_t newCapacity = oldCapacity ? oldCapacity*2 : INIT_CACHE_SIZE;
    if ((uint32_t)(mask_t)newCapacity != newCapacity) {
        // mask overflow - can't grow further
        // fixme this wastes one bit of mask
        newCapacity = oldCapacity;
    }
  // 釋放舊的空間,清空緩存列表夭咬,等待重新緩存
    reallocate(oldCapacity, newCapacity);
}

如果緩存列表滿(mǎn)了啃炸,會(huì)清空緩存列表,擴(kuò)容內(nèi)存為原來(lái)的兩倍卓舵,等待下次調(diào)用方法的時(shí)候重新進(jìn)行緩存南用,因?yàn)?_mask 存放的數(shù)量 已經(jīng)改變了,進(jìn)行 &運(yùn)算的時(shí)候掏湾,肯定是和之前的值不一樣裹虫,所以需要清空,重新緩存

5融击、總結(jié)

方法緩存的存儲(chǔ)方式 為散列表存儲(chǔ)
1.通過(guò)方法名 & 當(dāng)前的_mask 的數(shù)量 計(jì)算出下標(biāo)
2.將方法存放到下標(biāo)位置
3.如果當(dāng)前位置已經(jīng)存儲(chǔ)了別的方法筑公,那么將下標(biāo)-1,向上存儲(chǔ)
4.如果第一個(gè)位置也存儲(chǔ)了尊浪,就沖最后一個(gè)位置繼續(xù)
5.如果全部都滿(mǎn)了匣屡,就會(huì)重新分配內(nèi)存,重新進(jìn)行緩存


image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拇涤,一起剝皮案震驚了整個(gè)濱河市耸采,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌工育,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搓彻,死亡現(xiàn)場(chǎng)離奇詭異如绸,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)旭贬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)怔接,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人稀轨,你說(shuō)我怎么就攤上這事扼脐。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵瓦侮,是天一觀的道長(zhǎng)艰赞。 經(jīng)常有香客問(wèn)我,道長(zhǎng)肚吏,這世上最難降的妖魔是什么方妖? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮罚攀,結(jié)果婚禮上党觅,老公的妹妹穿的比我還像新娘。我一直安慰自己斋泄,他們只是感情好杯瞻,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著炫掐,像睡著了一般魁莉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上卒废,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天沛厨,我揣著相機(jī)與錄音,去河邊找鬼摔认。 笑死逆皮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的参袱。 我是一名探鬼主播电谣,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼抹蚀!你這毒婦竟也來(lái)了剿牺?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤环壤,失蹤者是張志新(化名)和其女友劉穎晒来,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體郑现,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡湃崩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了接箫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片攒读。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖辛友,靈堂內(nèi)的尸體忽然破棺而出薄扁,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布邓梅,位于F島的核電站脱盲,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏震放。R本人自食惡果不足惜宾毒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望殿遂。 院中可真熱鬧诈铛,春花似錦、人聲如沸墨礁。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)恩静。三九已至焕毫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間驶乾,已是汗流浹背邑飒。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留级乐,地道東北人疙咸。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像风科,于是被迫代替她去往敵國(guó)和親撒轮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345