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 ,拿到一个遍历器,然后进行位运算。