weak_table

前言

  • 系統(tǒng)為我們創(chuàng)建了一個(gè)全局的weak_table,這個(gè)表里面有一個(gè)weak_entries這樣的一個(gè)一維數(shù)組,
  • 這個(gè)weak_entries這個(gè)數(shù)組中的每個(gè)結(jié)構(gòu)體weak_entry_t,
  • 其中referent為被弱引用的對(duì)象, 而referrers則是指向這個(gè)弱引用的的地址

使用

使用weak還是__weak底層都是調(diào)用storeWeak這個(gè)函數(shù),區(qū)別在于模板的第一個(gè)參數(shù)HaveOld,官方解釋如下

If HaveOld is true, the variable has an existing value 
that needs to be cleaned up. This value might be nil.

重點(diǎn)函數(shù)

weak_register_no_lock函數(shù)

id weak_register_no_lock(weak_table_t *weak_table, id referent_id, 
                      id *referrer_id, bool crashIfDeallocating)
{
   // 被弱引用的對(duì)象
    objc_object *referent = (objc_object *)referent_id;
   // 指向弱引用對(duì)象的指針
    objc_object **referrer = (objc_object **)referrer_id;

    // now remember it and where it is being stored
    weak_entry_t *entry;
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        append_referrer(entry, referrer);
    } 
    else {
        weak_entry_t new_entry(referent, referrer);
        weak_grow_maybe(weak_table);
        weak_entry_insert(weak_table, &new_entry);
    }

    // Do not set *referrer. objc_storeWeak() requires that the 
    // value not change.

    return referent_id;
}

檢查weak_table中是否存在referent作為key的的weak_entry_t,如果存在,則插入一個(gè)新的指向這個(gè)弱引用對(duì)象的referrer地址,對(duì)應(yīng)的關(guān)系如下圖:

referrer 指向
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
    if (! entry->out_of_line()) {
        // Try to insert inline.
        for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            if (entry->inline_referrers[i] == nil) {
                entry->inline_referrers[i] = new_referrer;
                return;
            }
        }

        // Couldn't insert inline. Allocate out of line.
        weak_referrer_t *new_referrers = (weak_referrer_t *)
            calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t));
        // This constructed table is invalid, but grow_refs_and_insert
        // will fix it and rehash it.
        for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            new_referrers[i] = entry->inline_referrers[i];
        }
        entry->referrers = new_referrers;
        entry->num_refs = WEAK_INLINE_COUNT;
        entry->out_of_line_ness = REFERRERS_OUT_OF_LINE;
        entry->mask = WEAK_INLINE_COUNT-1;
        entry->max_hash_displacement = 0;
    }

    assert(entry->out_of_line());

    if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
        return grow_refs_and_insert(entry, new_referrer);
    }
    size_t begin = w_hash_pointer(new_referrer) & (entry->mask);
    size_t index = begin;
    size_t hash_displacement = 0;
    while (entry->referrers[index] != nil) {
        hash_displacement++;
        index = (index+1) & entry->mask;
        if (index == begin) bad_weak_table(entry);
    }
    if (hash_displacement > entry->max_hash_displacement) {
        entry->max_hash_displacement = hash_displacement;
    }
    weak_referrer_t &ref = entry->referrers[index];
    ref = new_referrer;
    entry->num_refs++;
}

沒有weak_entry_t存儲(chǔ)了referent的時(shí)候如何處理

        weak_entry_t new_entry(referent, referrer);
        weak_grow_maybe(weak_table);
        weak_entry_insert(weak_table, &new_entry);
static void weak_grow_maybe(weak_table_t *weak_table)
{
    size_t old_size = TABLE_SIZE(weak_table);

    // Grow if at least 3/4 full.
    if (weak_table->num_entries >= old_size * 3 / 4) {
        weak_resize(weak_table, old_size ? old_size*2 : 64);
    }
}

static void weak_resize(weak_table_t *weak_table, size_t new_size)
{
    size_t old_size = TABLE_SIZE(weak_table);
    
    weak_entry_t *old_entries = weak_table->weak_entries;
    weak_entry_t *new_entries = (weak_entry_t *)
        calloc(new_size, sizeof(weak_entry_t));
   // 因?yàn)閙ask為2^n,所以-1,是的mask等于全1的二進(jìn)制
    weak_table->mask = new_size - 1;
    weak_table->weak_entries = new_entries;
    weak_table->max_hash_displacement = 0;
    weak_table->num_entries = 0;  // restored by weak_entry_insert below
    
   // 重新將老的數(shù)據(jù)插入到插入到新分配的空間中
    if (old_entries) {
        weak_entry_t *entry;
        weak_entry_t *end = old_entries + old_size;
        for (entry = old_entries; entry < end; entry++) {
            if (entry->referent) {
                weak_entry_insert(weak_table, entry);
            }
        }
        free(old_entries);
    }
}

當(dāng)weak_table的num_entries大于總量的3/4,其中這個(gè)總量存儲(chǔ)在weak_table的mask字段中,初始使用64,以后每次擴(kuò)容為上次大小的2倍.
接下來插入這個(gè)新的weak_entry_t

static void weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry)
{
    weak_entry_t *weak_entries = weak_table->weak_entries;
    assert(weak_entries != nil);
   // 
    size_t begin = hash_pointer(new_entry->referent) & (weak_table->mask);
    size_t index = begin;
    size_t hash_displacement = 0;
    while (weak_entries[index].referent != nil) {
        index = (index+1) & weak_table->mask;
        if (index == begin) bad_weak_table(weak_entries);
        hash_displacement++;
    }
    
   // 這個(gè)index即為弱引用對(duì)象的地址,hash偏移后產(chǎn)生的
    weak_entries[index] = *new_entry;
    weak_table->num_entries++;

    if (hash_displacement > weak_table->max_hash_displacement) {
        weak_table->max_hash_displacement = hash_displacement;
    }
}

weak_entry_insert的算法算是__weak實(shí)現(xiàn)的精華所在,如果直接使用弱引用對(duì)象的地址作為index,那么weak_entries的大小就要alloc對(duì)應(yīng)系統(tǒng)位數(shù)的內(nèi)存大小,顯然不可能,這樣內(nèi)存空間將會(huì)全部被占用.因此出現(xiàn)了上面這個(gè)方法,根據(jù)存儲(chǔ)對(duì)象的數(shù)量,動(dòng)態(tài)申請(qǐng)內(nèi)存,再根據(jù)引用對(duì)象的地址mask后,一定是小于TABLE_SIZE,但是可能有兩個(gè)不同的對(duì)象,結(jié)尾的地址是相同的,這個(gè)時(shí)候就需要特殊處理,每次index++,直到這個(gè)index對(duì)應(yīng)的位置沒有被使用.

參考:
iOS __weak的底層實(shí)現(xiàn)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子彤蔽,更是在濱河造成了極大的恐慌横侦,老刑警劉巖郊霎,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件槐沼,死亡現(xiàn)場離奇詭異阶冈,居然都是意外死亡热鞍,警方通過查閱死者的電腦和手機(jī)葫慎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來薇宠,“玉大人偷办,你說我怎么就攤上這事〕胃郏” “怎么了椒涯?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長回梧。 經(jīng)常有香客問我逐工,道長,這世上最難降的妖魔是什么漂辐? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任泪喊,我火速辦了婚禮,結(jié)果婚禮上髓涯,老公的妹妹穿的比我還像新娘袒啼。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布蚓再。 她就那樣靜靜地躺著滑肉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪摘仅。 梳的紋絲不亂的頭發(fā)上靶庙,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音娃属,去河邊找鬼六荒。 笑死,一個(gè)胖子當(dāng)著我的面吹牛矾端,可吹牛的內(nèi)容都是我干的掏击。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼秩铆,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼砚亭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起殴玛,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤捅膘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后滚粟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寻仗,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年坦刀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蔬咬。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鲤遥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出林艘,到底是詐尸還是另有隱情盖奈,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布狐援,位于F島的核電站钢坦,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏啥酱。R本人自食惡果不足惜爹凹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望镶殷。 院中可真熱鬧禾酱,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至滓走,卻和暖如春垦江,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背搅方。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國打工比吭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人腰懂。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓梗逮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親绣溜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子慷彤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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