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