关注仓库,及时获得更新:iOS-Source-Code-AnalyzeFollow: Draveness · Github
由于 Objective-C 中的内存管理是一个比较大的话题,所以会分为两篇文章来对内存管理中的一些机制进行剖析,一部分分析自动释放池以及
autorelease
方法,另一部分分析retain
、release
方法的实现以及自动引用计数。
写在前面
在接口设计时,我们经常要考虑某些意义上的平衡。在内存管理中也是这样,Objective-C 同时为我们提供了增加引用计数的 retain
和减少引用计数的 release
方法。
这篇文章会在源代码层面介绍 Objective-C 中 retain
和 release
的实现,它们是如何达到平衡的。
从 retain 开始
如今我们已经进入了全面使用 ARC 的时代,几年前还经常使用的 retain
和 release
方法已经很难出现于我们的视野中了,绝大多数内存管理的实现细节都由编译器代劳。
在这里,我们还要从 retain
方法开始,对内存管理的实现细节一探究竟。
下面是 retain
方法的调用栈:
1 2 3 4 5 6 7 8 9 10 |
- [NSObject retain] └── id objc_object::rootRetain() └── id objc_object::rootRetain(bool tryRetain, bool handleOverflow) ├── uintptr_t LoadExclusive(uintptr_t *src) ├── uintptr_t addc(uintptr_t lhs, uintptr_t rhs, uintptr_t carryin, uintptr_t *carryout) ├── uintptr_t bits │ └── uintptr_t has_sidetable_rc ├── bool StoreExclusive(uintptr_t *dst, uintptr_t oldvalue, uintptr_t value) └── bool objc_object::sidetable_addExtraRC_nolock(size_t delta_rc) └── uintptr_t addc(uintptr_t lhs, uintptr_t rhs, uintptr_t carryin, uintptr_t *carryout) |
调用栈中的前两个方法的实现直接调用了下一个方法:
1 2 3 4 5 6 7 |
- (id)retain { return ((id)self)->rootRetain(); } id objc_object::rootRetain() { return rootRetain(false, false); } |
而 id objc_object::rootRetain(bool tryRetain, bool handleOverflow)
方法是调用栈中最重要的方法,其原理就是将 isa
结构体中的 extra_rc
的值加一。
extra_rc
就是用于保存自动引用计数的标志位,下面就是 isa
结构体中的结构:
接下来我们会分三种情况对 rootRetain
进行分析。
正常的 rootRetain
这是简化后的 rootRetain
方法的实现,其中只有处理一般情况的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
id objc_object::rootRetain(bool tryRetain, bool handleOverflow) { isa_t oldisa; isa_t newisa; do { oldisa = LoadExclusive(&isa.bits); newisa = oldisa; uintptr_t carry; newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); } while (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)); return (id)this; } |
在这里我们假设的条件是
isa
中的extra_rc
的位数足以存储retainCount
。
- 使用
LoadExclusive
加载isa
的值 - 调用
addc(newisa.bits, RC_ONE, 0, &carry)
方法将isa
的值加一 - 调用
StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)
更新isa
的值 - 返回当前对象
有进位版本的 rootRetain
在这里调用 addc
方法为 extra_rc
加一时,8 位的 extra_rc
可能不足以保存引用计数。
1 2 3 4 5 6 7 8 9 10 11 |
id objc_object::rootRetain(bool tryRetain, bool handleOverflow) { transcribeToSideTable = false; isa_t oldisa = LoadExclusive(&isa.bits); isa_t newisa = oldisa; uintptr_t carry; newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); if (carry && !handleOverflow) return rootRetain_overflow(tryRetain); } |
extra_rc
不足以保存引用计数,并且handleOverflow = false
。
当方法传入的 handleOverflow = false
时(这也是通常情况),我们会调用 rootRetain_overflow
方法:
1 2 3 |
id objc_object::rootRetain_overflow(bool tryRetain) { return rootRetain(tryRetain, true); } |
这个方法其实就是重新执行 rootRetain
方法,并传入 handleOverflow = true
。
有进位版本的 rootRetain(处理溢出)
当传入的 handleOverflow = true
时,我们就会在 rootRetain
方法中处理引用计数的溢出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
id objc_object::rootRetain(bool tryRetain, bool handleOverflow) { bool sideTableLocked = false; isa_t oldisa; isa_t newisa; do { oldisa = LoadExclusive(&isa.bits); newisa = oldisa; uintptr_t carry; newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); if (carry) { newisa.extra_rc = RC_HALF; newisa.has_sidetable_rc = true; } } while (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)); sidetable_addExtraRC_nolock(RC_HALF); return (id)this; } |
当调用这个方法,并且 handleOverflow = true
时,我们就可以确定 carry
一定是存在的了,
因为 extra_rc
已经溢出了,所以要更新它的值为 RC_HALF
:
1 |
#define RC_HALF (1ULL<<7) |
extra_rc
总共为 8 位,RC_HALF = 0b10000000
。
然后设置 has_sidetable_rc
为真,存储新的 isa
的值之后,调用 sidetable_addExtraRC_nolock
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
bool objc_object::sidetable_addExtraRC_nolock(size_t delta_rc) { SideTable& table = SideTables()[this]; size_t& refcntStorage = table.refcnts[this]; size_t oldRefcnt = refcntStorage; if (oldRefcnt & SIDE_TABLE_RC_PINNED) return true; uintptr_t carry; size_t newRefcnt = addc(oldRefcnt, delta_rc << SIDE_TABLE_RC_SHIFT, 0, &carry); if (carry) { refcntStorage = SIDE_TABLE_RC_PINNED | (oldRefcnt & SIDE_TABLE_FLAG_MASK); return true; } else { refcntStorage = newRefcnt; return false; } } |
这里我们将溢出的一位 RC_HALF
添加到 oldRefcnt
中,其中的各种 SIDE_TABLE
宏定义如下:
1 2 3 4 5 6 7 |