weak 修饰的变量,不会增加所引用对象的引用计数,且会在该对象析构后自动将指向置为nil。其本质是初始化和赋值操作会被编译器改写为 objc_initWeak
或者 objc_storeWeak
函数调用。objc_initWeak
的逻辑只是比 objc_storeWeak
多了所赋值对象的判空,接下来会直接调用 storeWeak(id *location, objc_object *newObj)
函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| static id storeWeak(id *location, objc_object *newObj) { id oldObj; SideTable *oldTable; SideTable *newTable;
if (haveOld) { oldObj = *location; oldTable = &SideTables()[oldObj]; } else { oldTable = nil; } if (haveNew) { newTable = &SideTables()[newObj]; } else { newTable = nil; }
SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
if (haveOld) weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
if (haveNew) { newObj = (objc_object *) weak_register_no_lock(&newTable->weak_table, (id)newObj, location, crashIfDeallocating);
if (newObj && !newObj->isTaggedPointer()) { newObj->setWeaklyReferenced_nolock(); } *location = (id)newObj; } SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable); return (id)newObj; }
|
查看 storeWeak
函数源码,如果weak指针已经有值,则会清除旧值,然后赋新值。清除旧值和赋值操作分别为 weak_unregister_no_lock
和 weak_register_no_lock
两个函数。
那入参和 SideTable
又是什么?
之前再看 @synchronized
的相关实现时,就已经发现 StripedMap
是个哈希表,而这里有一个全局的 StripedMap
,key 是一个对象,value 就是 SideTable
。SideTable
及其属性的结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| struct SideTable { spinlock_t slock; RefcountMap refcnts; weak_table_t weak_table; };
struct weak_table_t { weak_entry_t *weak_entries; size_t num_entries; uintptr_t mask; uintptr_t max_hash_displacement; };
struct weak_entry_t { DisguisedPtr<objc_object> referent; union { struct { weak_referrer_t *referrers; uintptr_t out_of_line_ness : 2; uintptr_t num_refs : PTR_MINUS_2; uintptr_t mask; uintptr_t max_hash_displacement; }; struct { weak_referrer_t inline_referrers[WEAK_INLINE_COUNT]; }; };
typedef DisguisedPtr<objc_object *> weak_referrer_t;
|
SideTable
是对 weak_table_t
的封装,除了持有 weak_table_t
变量,还持有一个自旋锁来保证线程安全。
总结
内存中维护着一个全局的哈希表,以对象的引用为 key,value 是一个SideTable
,SideTable
有一个自旋锁,有存放引用计数,还有一个存放着 weak 修饰的指针地址数组。
对于 TaggedPointer
对象,并不会设置 isa 中的弱引用标记位