Objective-C weak深入理解

1、weak是弱引用,所引用的对象计数不会加1。

2、weak变量在其引用的对象被销毁之后,会被置为nil。

3、weak通常用于block, delegate, NSTimer,以解决循环引用带来的内存泄漏问题。

NSObject *obj = [[NSObject alloc] init]; // obj是被引用对象的指针
id __weak obj1 = obj; // obj1是weak变量,也是被引用对象的指针

weak的底层实现,简化的源码及解析如下: 

id objc_storeWeak(id *location, id newObj) // objc_storeWeak(&obj1, obj)
{   
    id oldObj;
    SideTable *oldTable;
    SideTable *newTable;
 
    oldObj = *location; // weak变量,也是被引用对象的指针
    // 根据被引用对象的指针的哈希值得到对应的SideTable
    oldTable = SideTable::tableForPointer(oldObj);
    newTable = SideTable::tableForPointer(newObj);

    if (oldObj) {
        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location); // 删掉旧引用,因为location要指向newObj,其实应该先判断oldObj != newObj的
    }
    if (newObj) {
        newObj = weak_register_no_lock(&newTable->weak_table, newObj,location); // 添加新引用
    }
    *location = newObj;

    return newObj;
} 

 

void weak_unregister_no_lock(weak_table_t *weak_table, id referent_id, id *referrer_id)
{
    objc_object *referent = (objc_object *)referent_id;
    objc_object **referrer = (objc_object **)referrer_id;

    weak_entry_t *entry;
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        remove_referrer(entry, referrer); // 删掉旧的weak变量的指针
        bool empty = true;
        for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            if (entry->inline_referrers[i]) {
                empty = false; 
                break;
            }
        }
        if (empty) {
            weak_entry_remove(weak_table, entry); // 如果旧的被引用对象的指针对应的weak变量的指针数组空了,则删掉这个被引用对象的指针
        }
    }
}

   

id weak_register_no_lock(weak_table_t *weak_table, id referent_id, id *referrer_id)
{
    // 被引用对象的指针
    objc_object *referent = (objc_object *)referent_id;
    // weak变量的指针
    objc_object **referrer = (objc_object **)referrer_id;

    weak_entry_t *entry;
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        append_referrer(entry, referrer); // 有就往指针数组加一个
    } else {
        weak_entry_t new_entry;
        new_entry.referent = referent;
        new_entry.inline_referrers[0] = referrer;
        for (size_t i = 1; i < WEAK_INLINE_COUNT; i++) {
            new_entry.inline_referrers[i] = nil;
        }
        weak_grow_maybe(weak_table);
        weak_entry_insert(weak_table, &new_entry); // 没有就创建一个指针数组,然后加一个
    }

    return referent_id;
}

 

class SideTable {
private:
    static uint8_t table_buf[SIDE_TABLE_STRIPE * SIDE_TABLE_SIZE]; // 所有SideTable对象共用,数组元素是SideTable *。看成全局数组,而不属于某个SideTable对象,更好理解。

public:
    weak_table_t weak_table; // SideTable对象和weak表一一对应
    
    static SideTable *tableForPointer(const void *p) 
    {
        uintptr_t a = (uintptr_t)p;
        int index = ((a >> 4) ^ (a >> 9)) & (SIDE_TABLE_STRIPE - 1);
        return (SideTable *)&table_buf[index * SIDE_TABLE_SIZE];
    }
    
};

 

// weak表
struct weak_table_t {
    weak_entry_t *weak_entries;
};

 

// weak表项
struct weak_entry_t {
    DisguisedPtr<objc_object> referent; // 被引用对象的指针
    weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT]; // weak变量的指针数组
};

来个直观的整体结构图如下: 

  

参考链接:

https://opensource.apple.com/source/objc4/objc4-532/runtime/NSObject.mm.auto.html

https://opensource.apple.com/source/objc4/objc4-646/runtime/objc-weak.h

 

posted @ 2018-06-25 22:32  happyyoung  阅读(1062)  评论(0编辑  收藏  举报