iOS isa 引用計數管理
之前文章提得到過在 arm64 之后,蘋果對isa指針進行了優化,采用 共用體的方式,nopointer 的方式來管理 isa,也就是說,isa 指針不僅僅只存放地址,也存放著引用計數相關的東西,我們來看源碼
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
isa 指針的定義大概是這樣的
1. nonpointer,0代表普通的指針,存儲著class ,meta-class 對象的內存地址,1代表優化過,使用位域存儲更多的信息。
2. has_cxx_dtor : 是否有 C++ 的析構函數,如果沒有,釋放的時候回更快
3. shiftcls,存儲著 class ,meta-class 對象的內存地址信息。
4. magic:用于在調試時分辨對象是否未完成初始化
5. weakly_referenced:是否有被弱引用指向過,如果沒有,釋放時候會更快
6. deallocating 對象是否正在釋放
7. extra_rc 里面存儲的值是引用計數減一
8. has_sidetable_rc:引用計數是否過大無法存儲在 isa 中,如果為1,那么引用計數會存儲在一個叫 SideTable 的類的屬性中。
9. has_assoc : 是否有設置過關聯對象,如果沒有,釋放會更快
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;
weak_table_t weak_table;
}
我們來看下 SideTable 這個類,RefcountMap 存儲著引用計數,是個散列表,根據一個 key 就可以找到對應的 value,
objc_object::rootRetainCount()
{
if (isTaggedPointer()) return (uintptr_t)this;
sidetable_lock();
isa_t bits = LoadExclusive(&isa.bits);
ClearExclusive(&isa.bits);
if (bits.nonpointer) {
uintptr_t rc = 1 + bits.extra_rc;
if (bits.has_sidetable_rc) {
rc += sidetable_getExtraRC_nolock();
}
sidetable_unlock();
return rc;
}
sidetable_unlock();
return sidetable_retainCount();
}
objc_object::sidetable_getExtraRC_nolock()
{
assert(isa.nonpointer);
SideTable& table = SideTables()[this];
RefcountMap::iterator it = table.refcnts.find(this);
if (it == table.refcnts.end()) return 0;
else return it->second >> SIDE_TABLE_RC_SHIFT;
}
retainCount
這個源碼是獲取 retainCount , 可以看到,如果是 tagged pointer 類型的,那么直接返回,什么事tagged pointer ,我之前文章也有說過,接下來,去拿到 isa ,isa.bits,就代表拿到 isa,首先看下是否為 非指針類型也就是優化過的 isa,如果是的話,那么久直接將 extra_rc + 1,也就是引用計數加一,如果 bits.has_sidetable_rc 為1,那么他的引用計數是存儲在 SideTable 里面的,不是存儲在 isa 中,從 方法中可以看到,是先從 SideTables里面穿進去一個 key,拿到 SideTable ,然后又拿到這個SideTable 的 refcnts,也就是那個存放引用計數的散列表,然后再出入一個 key ,拿到一個遍歷器,然后進行位運算。