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