对象回收时 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 中,也存在一定的性能开销。

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