在上一篇文章中,咱们从源码上来看了下苹果是如何实现一系列方法的,这篇文章将继续分析 retainCount、retain 和 release 方法。c++
retainCount 是获取当前对象引用计数的方法,其实现以下:less
- (NSUInteger)retainCount {
return ((id)self)->rootRetainCount();
}
复制代码
直接调用了 rootRetainCount 方法,实现以下:ide
inline uintptr_t
objc_object::rootRetainCount()
{
if (isTaggedPointer()) return (uintptr_t)this; // 若是是 tagged pointer,直接返回 this
sidetable_lock(); // 为 sidetable 加锁
isa_t bits = LoadExclusive(&isa.bits); // 经过 LoadExclusive 方法加载 isa 的值,加锁
ClearExclusive(&isa.bits); // 解锁
if (bits.nonpointer) { // 若是 isa 不是经过指针方式实现的话
uintptr_t rc = 1 + bits.extra_rc; // 获取当前对象的引用计数
if (bits.has_sidetable_rc) { // 若是当前对象有额外的引用计数
rc += sidetable_getExtraRC_nolock(); // 加上额外的引用计数
}
sidetable_unlock(); // 为 sidetable 解锁
return rc;
}
sidetable_unlock(); // 为 sidetable 解锁
return sidetable_retainCount(); // 返回 sidetable 中存储的 retainCount
}
复制代码
rootRetainCount 方法作的事情已经写到上面了,接下来来看一下其中几个重要函数(方法)的实现:函数
LoadExclusive 的做用是让读取操做原子化,根据 CPU 不一样实现不一样,好比在 x86-64 上就是单纯的直接返回值,而在 arm64 上则使用了 ldxr(load exclusive register) 指令。以下所示:ui
static ALWAYS_INLINE uintptr_t LoadExclusive(uintptr_t *src) {
uintptr_t result;
asm("ldxr %" p "0, [%x1]"
: "=r" (result)
: "r" (src), "m" (*src));
return result;
}
复制代码
同理,ClearExclusive 则是解除锁,使用了 clrex 指令,以下所示:this
static ALWAYS_INLINE void ClearExclusive(uintptr_t *dst) {
// pretend it writes to *dst for instruction ordering purposes
asm("clrex" : "=m" (*dst));
}
复制代码
extra_rc 就是用于保存自动引用计数的标志位,是在 isa_t 结构体中,共有8位。它存储的是对象自己以外的引用计数的数量,因此获取总数的时候要加 1。但 extra_rc 可能不足以存储引用计数,这时候 sidetable 就派上用场了。spa
has_sidetable_rc 是用于标志是否经过 sidetable 存储引用计数的标志。debug
sidetable_getExtraRC_nolock 函数是用于从 sidetable 中获取引用计数信息的方法,实现以下:指针
size_t
objc_object::sidetable_getExtraRC_nolock()
{
assert(isa.nonpointer);
SideTable& table = SideTables()[this]; // 根据 this 找到引用计数存在的 table
RefcountMap::iterator it = table.refcnts.find(this); // 查找引用计数
if (it == table.refcnts.end()) return 0; // 若是没找到,返回0
else return it->second >> SIDE_TABLE_RC_SHIFT; // 若是找到了,经过 SIDE_TABLE_RC_SHIFT 位掩码获取对应的引用计数
}
复制代码
若是 nonpointer 为 true,表示使用的仍是老版本中经过指针表示 isa 的方式,这个时候,全部的引用计数所有存在 sidetable 中,以下所示:code
uintptr_t
objc_object::sidetable_retainCount()
{
SideTable& table = SideTables()[this]; // 根据 this 找到引用计数存在的 table
size_t refcnt_result = 1; // 设置对象自己的引用计数为1
table.lock(); // 加锁
RefcountMap::iterator it = table.refcnts.find(this); // 查找引用计数
if (it != table.refcnts.end()) { // 若是找到了
// this is valid for SIDE_TABLE_RC_PINNED too
refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT; // 返回 1 + sidetable 中存储的引用计数
}
table.unlock(); // 解锁
return refcnt_result;
}
复制代码
retain 的主要做用是增长引用计数,实现以下:
// Replaced by ObjectAlloc
- (id)retain {
return ((id)self)->rootRetain();
}
ALWAYS_INLINE id
objc_object::rootRetain()
{
return rootRetain(false, false);
}
ALWAYS_INLINE id
objc_object::rootRetain(bool tryRetain, bool handleOverflow)
{
if (isTaggedPointer()) return (id)this; // 若是是 tagged pointer 直接返回 this
bool sideTableLocked = false; // 标记 sideTable 是否处于加锁状态
bool transcribeToSideTable = false; // 是否须要将引用计数存储在 sideTable 中
isa_t oldisa;
isa_t newisa;
do {
transcribeToSideTable = false;
oldisa = LoadExclusive(&isa.bits); // 取出 isa,加锁
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) { // 若是 isa 是经过指针方式实现的
ClearExclusive(&isa.bits); // 解锁
if (!tryRetain && sideTableLocked) sidetable_unlock(); // 若是 tryRetain 为 false 且给 sideTable 加过锁,则为其解锁
if (tryRetain) return sidetable_tryRetain() ? (id)this : nil; // 若是 tryRetain 为 ture,则根据调用 sidetable_tryRetain() 结果决定返回 this 或者是 nil。
else return sidetable_retain(); // 其他状况直接返回 sidetable_retain 的结果
}
// don't check newisa.fast_rr; we already called any RR overrides
if (slowpath(tryRetain && newisa.deallocating)) { // 若是 tryRetain 为 true,且对象正在被销毁,则执行如下代码
ClearExclusive(&isa.bits); // 解锁
if (!tryRetain && sideTableLocked) sidetable_unlock(); // 若是 tryRetain 为 false 且给 sideTable 加过锁,则为其解锁
return nil;
}
uintptr_t carry;
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
if (slowpath(carry)) { // 若是 carry 为 true,要处理引用计数溢出的状况
// newisa.extra_rc++ overflowed
if (!handleOverflow) { // 若是 handleOverflow 为 false
ClearExclusive(&isa.bits); // 解锁
return rootRetain_overflow(tryRetain); // 跳转执行 rootRetain_overflow
}
// Leave half of the retain counts inline and
// prepare to copy the other half to the side table.
if (!tryRetain && !sideTableLocked) sidetable_lock(); // 若是 sidetable_lock 未加锁,则为其加锁
sideTableLocked = true;
transcribeToSideTable = true;
newisa.extra_rc = RC_HALF;
newisa.has_sidetable_rc = true; // 标记是否在 sidetable 中存有引用计数
}
} while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));
if (slowpath(transcribeToSideTable)) { // 若是须要讲部分的引用计数放到 sidetable 中
// Copy the other half of the retain counts to the side table.
sidetable_addExtraRC_nolock(RC_HALF);
}
if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock(); // 解锁
return (id)this;
}
复制代码
关于 retain 方法每一步作了什么都已经写到注释里了,接下来来看几个知识点:
这两个方法是成对儿使用的,做用是给 sidetable 加锁/解锁
void
objc_object::sidetable_lock()
{
SideTable& table = SideTables()[this];
table.lock();
}
void
objc_object::sidetable_unlock()
{
SideTable& table = SideTables()[this];
table.unlock();
}
复制代码
sidetable_tryRetain 的实现以下:
bool
objc_object::sidetable_tryRetain()
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this];
// NO SPINLOCK HERE
// _objc_rootTryRetain() is called exclusively by _objc_loadWeak(),
// which already acquired the lock on our behalf.
// fixme can't do this efficiently with os_lock_handoff_s
// if (table.slock == 0) {
// _objc_fatal("Do not call -_tryRetain.");
// }
bool result = true;
RefcountMap::iterator it = table.refcnts.find(this); // 经过 this 从 table 中寻找是否有引用计数
if (it == table.refcnts.end()) { // 若是查找不到
table.refcnts[this] = SIDE_TABLE_RC_ONE; // 设置表中的引用计数为 SIDE_TABLE_RC_ONE
} else if (it->second & SIDE_TABLE_DEALLOCATING) { // 若是对象此时处于 deallocating 状态
result = false; // 结果设为 false
} else if (! (it->second & SIDE_TABLE_RC_PINNED)) { // 若是查找到了,且未溢出,则将引用计数加1。
it->second += SIDE_TABLE_RC_ONE;
}
return result;
}
复制代码
Objecitve-C 定义了几个重要的偏移量,用于取出对应的值,定义以下
// The order of these bits is important.
#define SIDE_TABLE_WEAKLY_REFERENCED (1UL<<0)
#define SIDE_TABLE_DEALLOCATING (1UL<<1) // MSB-ward of weak bit
#define SIDE_TABLE_RC_ONE (1UL<<2) // MSB-ward of deallocating bit
#define SIDE_TABLE_RC_PINNED (1UL<<(WORD_BITS-1))
#define SIDE_TABLE_RC_SHIFT 2
#define SIDE_TABLE_FLAG_MASK (SIDE_TABLE_RC_ONE-1)
复制代码
实现以下:
id
objc_object::sidetable_retain()
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this];
table.lock();
size_t& refcntStorage = table.refcnts[this];
if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) { // 若是查找到了,且未溢出,则将引用计数加1。
refcntStorage += SIDE_TABLE_RC_ONE;
}
table.unlock();
return (id)this;
}
复制代码
这个函数的做用就是引用计数加一,其实现以下:
static ALWAYS_INLINE uintptr_t addc(uintptr_t lhs, uintptr_t rhs, uintptr_t carryin, uintptr_t *carryout) {
return __builtin_addcl(lhs, rhs, carryin, carryout);
}
复制代码
__builtin_addcl 是 Clang Language Extensions,可是我没找到它的定义,大概是大整数加法吧。
当 addc 方法告知咱们有溢出状况出现时,咱们须要经过 rootRetain_overflow 方法处理溢出的状况,实现以下:
NEVER_INLINE id
objc_object::rootRetain_overflow(bool tryRetain)
{
return rootRetain(tryRetain, true);
}
复制代码
本质上属于递归调用,又回到了这个方法。
NEVER_INLINE 的定义以下:
#define NEVER_INLINE inline __attribute__((noinline))
复制代码
咱们在上一篇文章里已经介绍过 inline 了,在此就不赘述了。
当咱们肯定引用计数已经溢出的时候,经过 newisa.extra_rc = RC_HALF; 将其设置为溢出值。RC_HALF 定义以下:
#define RC_HALF (1ULL<<7)
复制代码
extra_rc 总共为 8 位,RC_HALF = 0b10000000。
其实现以下:
static ALWAYS_INLINE bool StoreExclusive(uintptr_t *dst, uintptr_t oldvalue, uintptr_t value) {
return __sync_bool_compare_and_swap((void **)dst, (void *)oldvalue, (void *)value);
}
复制代码
__sync 开头的方法是原子操做,这个函数是用来作比较后操做数的原子操做,若是 dst == oldvalue,就将 value 写入 dst,并在相等并写入的状况下返回 true。
sidetable_addExtraRC_nolock 方法用于将溢出的引用计数加到 sidetable 中。实现以下:
// Move some retain counts to the side table from the isa field.
// Returns true if the object is now pinned.
bool
objc_object::sidetable_addExtraRC_nolock(size_t delta_rc)
{
assert(isa.nonpointer);
SideTable& table = SideTables()[this];
size_t& refcntStorage = table.refcnts[this];
size_t oldRefcnt = refcntStorage;
// isa-side bits should not be set here
assert((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0);
assert((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0);
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;
}
}
复制代码
在了解完 retain 的实现以后,release 的实现就比较容易解读了,基本就是 retain 的反操做,实现以下:
-(void) release
{
_objc_rootRelease(self);
}
void
_objc_rootRelease(id obj)
{
assert(obj);
obj->rootRelease();
}
ALWAYS_INLINE bool
objc_object::rootRelease()
{
return rootRelease(true, false);
}
ALWAYS_INLINE bool
objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
{
if (isTaggedPointer()) return false;
bool sideTableLocked = false; // 标识位,用于标记 sideTable 是否加锁
isa_t oldisa;
isa_t newisa;
retry:
do {
oldisa = LoadExclusive(&isa.bits); // 为 isa.bits 加锁
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) { // 若是 newisa 是经过指针实现的
ClearExclusive(&isa.bits); // 为 isa.bits 解锁
if (sideTableLocked) sidetable_unlock(); // 若是 sideTableLocked 处于被加锁状态,为其解锁
return sidetable_release(performDealloc); // 经过 sidetable_release 为其解锁
}
// don't check newisa.fast_rr; we already called any RR overrides
uintptr_t carry;
newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry); // extra_rc--
if (slowpath(carry)) { // 若是发现溢出的状况
// don't ClearExclusive()
goto underflow;
}
} while (slowpath(!StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits)));
if (slowpath(sideTableLocked)) sidetable_unlock(); // 解锁
return false;
underflow:
// newisa.extra_rc-- underflowed: borrow from side table or deallocate
// abandon newisa to undo the decrement
newisa = oldisa;
if (slowpath(newisa.has_sidetable_rc)) {
if (!handleUnderflow) {
ClearExclusive(&isa.bits); // 接触 isa.bits 的锁
return rootRelease_underflow(performDealloc); // 处理下溢的状况
}
// Transfer retain count from side table to inline storage.
if (!sideTableLocked) {
ClearExclusive(&isa.bits); // 解除 isa.bits 的锁
sidetable_lock(); // 为 sidetable_lock 加锁
sideTableLocked = true; // 标记 sideTable 被加锁
// Need to start over to avoid a race against
// the nonpointer -> raw pointer transition.
goto retry;
}
// Try to remove some retain counts from the side table.
size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF); // 将 sidetable 中的引用计数减一
// To avoid races, has_sidetable_rc must remain set
// even if the side table count is now zero.
if (borrowed > 0) {
// Side table retain count decreased.
// Try to add them to the inline count.
newisa.extra_rc = borrowed - 1; // redo the original decrement too
bool stored = StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits); // 存储更改后的 isa.bits
if (!stored) { // 若是存储失败,即刻重试一次
// Inline update failed.
// Try it again right now. This prevents livelock on LL/SC
// architectures where the side table access itself may have
// dropped the reservation.
isa_t oldisa2 = LoadExclusive(&isa.bits);
isa_t newisa2 = oldisa2;
if (newisa2.nonpointer) {
uintptr_t overflow;
newisa2.bits =
addc(newisa2.bits, RC_ONE * (borrowed-1), 0, &overflow);
if (!overflow) {
stored = StoreReleaseExclusive(&isa.bits, oldisa2.bits,
newisa2.bits);
}
}
}
if (!stored) { // 若是仍是更新失败,则回滚操做并 retry
// Inline update failed.
// Put the retains back in the side table.
sidetable_addExtraRC_nolock(borrowed);
goto retry;
}
// Decrement successful after borrowing from side table.
// This decrement cannot be the deallocating decrement - the side
// table lock and has_sidetable_rc bit ensure that if everyone
// else tried to -release while we worked, the last one would block.
sidetable_unlock(); // 解锁
return false;
}
else {
// Side table is empty after all. Fall-through to the dealloc path.
}
}
// Really deallocate.
if (slowpath(newisa.deallocating)) { // 若是当前 newisa 处于 deallocating 状态
ClearExclusive(&isa.bits); // 解除 isa.bits 的锁
if (sideTableLocked) sidetable_unlock(); // 解除 sidetable 的锁
return overrelease_error(); // 返回 overrelease_error 的结果值
// does not actually return
}
newisa.deallocating = true; // 设置 deallocating 为 true
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry; // 若是存储失败,继续重试
if (slowpath(sideTableLocked)) sidetable_unlock(); // 解除 sidetable 的锁
__sync_synchronize();
if (performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return true;
}
复制代码
sidetable_release 是为 sidetable 中存储的引用计数执行减1操做的方法,实现以下:
// rdar://20206767
// return uintptr_t instead of bool so that the various raw-isa
// -release paths all return zero in eax
uintptr_t
objc_object::sidetable_release(bool performDealloc)
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this]; // 经过对象获取对应的 table
bool do_dealloc = false; // 标识是否须要执行 dealloc 方法
table.lock(); // 加锁
RefcountMap::iterator it = table.refcnts.find(this); // 经过 this 查找引用计数
if (it == table.refcnts.end()) { // 未找到
do_dealloc = true;
table.refcnts[this] = SIDE_TABLE_DEALLOCATING; // 设为 deallocating 状态
} else if (it->second < SIDE_TABLE_DEALLOCATING) { // 若是对象处于 deallocating 状态
// SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it.
do_dealloc = true;
it->second |= SIDE_TABLE_DEALLOCATING;
} else if (! (it->second & SIDE_TABLE_RC_PINNED)) { // 若是查找到了对象的引用计数
it->second -= SIDE_TABLE_RC_ONE; // 引用计数加一
}
table.unlock(); // 解锁
if (do_dealloc && performDealloc) { // 若是符合调用 dealloc 方法的状况下
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc); // 经过 objc_msgSend 执行 dealloc 方法
}
return do_dealloc;
}
复制代码
就是 addc 的反操做,实现以下:
static ALWAYS_INLINE uintptr_t subc(uintptr_t lhs, uintptr_t rhs, uintptr_t carryin, uintptr_t *carryout) {
return __builtin_subcl(lhs, rhs, carryin, carryout);
}
复制代码
用于处理下溢的状况,实现以下
NEVER_INLINE bool
objc_object::rootRelease_underflow(bool performDealloc)
{
return rootRelease(performDealloc, true);
}
复制代码
sidetable_subExtraRC_nolock 用来将 sidetable 中存储的引用计数减一,实现以下:
// Move some retain counts from the side table to the isa field.
// Returns the actual count subtracted, which may be less than the request.
size_t
objc_object::sidetable_subExtraRC_nolock(size_t delta_rc)
{
assert(isa.nonpointer);
SideTable& table = SideTables()[this];
RefcountMap::iterator it = table.refcnts.find(this);
if (it == table.refcnts.end() || it->second == 0) {
// Side table retain count is zero. Can't borrow.
return 0;
}
size_t oldRefcnt = it->second;
// isa-side bits should not be set here
assert((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0); // 确保对象不是 deallocating 状态
assert((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0); // 确保是否是弱饮用
size_t newRefcnt = oldRefcnt - (delta_rc << SIDE_TABLE_RC_SHIFT); // 引用计数减一
assert(oldRefcnt > newRefcnt); // shouldn't underflow
it->second = newRefcnt;
return delta_rc; // 返回引用计数的变化
}
复制代码
StoreReleaseExclusive 内部直接调用了咱们以前提过的 StoreExclusive 方法,实现以下:
static ALWAYS_INLINE bool StoreReleaseExclusive(uintptr_t *dst, uintptr_t oldvalue, uintptr_t value) {
return StoreExclusive(dst, oldvalue, value);
}
复制代码
overrelease_error 是用来在过分调用 release 的时候报错用的,实现以下:
NEVER_INLINE
bool
objc_object::overrelease_error()
{
_objc_inform_now_and_on_crash("%s object %p overreleased while already deallocating; break on objc_overrelease_during_dealloc_error to debug", object_getClassName((id)this), this);
objc_overrelease_during_dealloc_error();
return false; // allow rootRelease() to tail-call this
}
BREAKPOINT_FUNCTION(
void objc_overrelease_during_dealloc_error(void)
);
复制代码
__sync_synchronize 的做用是:
No memory operand will be moved across the operation, either forward or backward. Further, instructions will be issued as necessary to prevent the processor from speculating loads across the operation and from queuing stores after the operation.
简而言之就是,在这个方法调用以后,涉及到内存的运算符都被禁止操做了(由于要进行析构了)。
以上就是关于 retainCount、retain 和 release 的所有源码解读了。