對象回收時 Weak 指針自動被置爲 nil 是如何實現的

前言

    我們都知道 Weak 指針不會增加所引用對象的計數,並在引用對象被回收的時候自動被置爲 nil 。通常用於解決循環引用問題。那麼,自動被置爲 nil 是如何實現的呢?答案是 Weak 表。

Weak 表

    Runtime 維護了一個 Weak 表,用於存儲所有 Weak 指針。Weak 表是一個哈希表,Key 是對象的地址,Value 是一個數組,數組裏面放的是 Weak 指針的地址(這個地址的值是所指對象的地址)。

    在對象被回收的時候,經過層層調用,會最終觸發下面的方法將所有Weak指針的值設爲 nil 。(具體定義在objc-weak.m中)

PRIVATE_EXTERN void 
arr_clear_deallocating(weak_table_t *weak_table, id referent) {
    {
        weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
        if (entry == NULL) {
            /// XXX shouldn't happen, but does with mismatched CF/objc
            //printf("XXX no entry for clear deallocating %p\n", referent);
            return;
        }
        // zero out references
        for (int i = 0; i < entry->referrers.num_allocated; ++i) {
            id *referrer = entry->referrers.refs[i].referrer;
            if (referrer) {
                if (*referrer == referent) {
                    *referrer = nil;
                }
                else if (*referrer) {
                    _objc_inform("__weak variable @ %p holds %p instead of %p\n", referrer, *referrer, referent);
                }
            }
        }

        weak_entry_remove_no_lock(weak_table, entry);
        weak_table->num_weak_refs--;
    }
}

    簡單來說,這個方法首先根據對象地址獲取所有 Weak 指針地址的數組,然後遍歷這個數組,把每個地址存儲的數據設爲 nil ,最後把這個 key-value entry 從 Weak 表中刪除。

    這裏只簡單說說對象回收時,Weak 指針如何設爲 nil,至於 Weak 指針如何註冊到 Weak 表中、如何維護可以參考 objc-weak.m 中的其它源碼。

注意

    1. 從實現中可以看出,Weak 指針的使用涉及到 Hash 表的增刪改查,存在一定的性能開銷。
    2. 使用 Weak 指針的時候,應首先獲取一個 Strong 指針再使用。倒不是爲了防止在使用過程中,對象被回收,形成野指針。 這個不用擔心,因爲你使用了 Weak 指針,對象就會被加入到 autoreleasepool 中,可以放心使用。但是要注意的是,如果在一個代碼塊中頻繁使用 Weak 指針,還是應首先獲取一個 Strong 指針,否則這個對象會被一次又一次的加入 autoreleasepool 中,也存在一定的性能開銷。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章