516 字
3 分钟
SideTable和weak底层实现
我们先看一个小题目,看看下面的输出回事什么样的?
NSObject *objc = [[NSObject alloc] init];
NSLog(@"%zd - %@",(long)CFGetRetainCount((__bridge CFTypeRef)(objc)), objc);
__weak typeof(id) weakObjc = objc;
NSLog(@"%zd - %@",(long)CFGetRetainCount((__bridge CFTypeRef)(objc)), objc);
NSLog(@"%zd - %@",(long)CFGetRetainCount((__bridge CFTypeRef)(weakObjc)), weakObjc);输出:
1、1、2。是不是很迷惑?别急我们慢慢来
前言
前面在对内存管理进行研究的时候接触到了 SideTalbe ,今天我们也依然会和他见面,它究竟是什么呢?
voidobjc_object::sidetable_lock(){ SideTable& table = SideTables()[this]; table.lock();}SideTable 可以看出, SideTable 是有多张的。它是一个 HashMap 。
一、 SideTable 的结构
struct SideTable { spinlock_t slock; // 引用计数 Map RefcountMap refcnts; // 若引用 Map weak_table_t weak_table;
SideTable() { memset(&weak_table, 0, sizeof(weak_table)); }
~SideTable() { _objc_fatal("Do not delete SideTable."); }
void lock() { slock.lock(); } void unlock() { slock.unlock(); } void forceReset() { slock.forceReset(); }
...};简单了解了 SideTable 后我们继续回到题目。
二、 weak 做了什么
2.1 调试
在这里打上汇编断点

走到了 objc_initWeak

objc_initWeak

storeWeak

weak_register_no_lock
注册一个 对象 - weak指针 对。如果没有的话,创建一个 weak 的 entry 对象。
参数:
- weak_table : SideTable 地址
- referent : 被弱引用的对象
- referrer : 弱引用指针地址

append_referrer
把 referrer 加进 entry 的 inline_referrers

这里又见到了熟悉的 3/4 扩容
weak_entry_t
struct weak_entry_t { DisguisedPtr<objc_object> referent; union { struct { // 一个对象可以有多个 referent 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 { // out_of_line_ness field is low bits of inline_referrers[1] weak_referrer_t inline_referrers[WEAK_INLINE_COUNT]; }; };
...
weak_entry_t(objc_object *newReferent, objc_object **newReferrer) : referent(newReferent) { inline_referrers[0] = newReferrer; for (int i = 1; i < WEAK_INLINE_COUNT; i++) { inline_referrers[i] = nil; } }};三、 回到题目继续分析
我们在这里下个断点,并打开汇编断点:

来到了 objc_loadWeakRetained

objc_loadWeakRetained
内部继续跟进来到了 rootTryRetain ,这里内部引用计数会加

总结
weak原理- 得到
SideTable的弱引用表 - 得到一个
weak_entry_t - 把
referent加入到entry的数组inline_referrers - 如果
weak_table需要扩容- 3/4 扩容,类似与方法列表
- 把
new_entry加入到weak_table中
- 得到
- 当我们使用一个
weakObjc的时候,会调用rootTryRetain,所以引用计数会 +1。也就是这里我们看到输出为 2 的原因所在。