苹果将ISA设计成了联合体,在ISA中存储了与该对象相关的一些内存信息,因为 并不需要64个二进制全部都用来存储指针
ISA 的结构
// x86_64 架构
struct {
uintptr_t nonpointer : 1; // 0:普通指针,1:优化过,使用位域存储更多信息
uintptr_t has_assoc : 1; // 对象是否含有或曾经含有关联引用
uintptr_t has_cxx_dtor : 1; // 表示是否有C++析构函数或OC的dealloc
uintptr_t shiftcls : 44; // 存放着 Class、Meta-Class 对象的内存地址信息
uintptr_t magic : 6; // 用于在调试时分辨对象是否未完成初始化
uintptr_t weakly_referenced : 1; // 是否被弱引用指向
uintptr_t deallocating : 1; // 对象是否正在释放
uintptr_t has_sidetable_rc : 1; // 是否需要使用 sidetable 来存储引用计数
uintptr_t extra_rc : 8; // 引用计数能够用 8 个二进制位存储时,直接存储在这里
};
// arm64 架构
struct {
uintptr_t nonpointer : 1; // 0:普通指针,1:优化过,使用位域存储更多信息
uintptr_t has_assoc : 1; // 对象是否含有或曾经含有关联引用
uintptr_t has_cxx_dtor : 1; // 表示是否有C++析构函数或OC的dealloc
uintptr_t shiftcls : 33; // 存放着 Class、Meta-Class 对象的内存地址信息
uintptr_t magic : 6; // 用于在调试时分辨对象是否未完成初始化
uintptr_t weakly_referenced : 1; // 是否被弱引用指向
uintptr_t deallocating : 1; // 对象是否正在释放
uintptr_t has_sidetable_rc : 1; // 是否需要使用 sidetable 来存储引用计数
uintptr_t extra_rc : 19; // 引用计数能够用 19 个二进制位存储时,直接存储在这里
};
这里的 has_sidetable_rc 和 extra_rc, has_sidetable_rc 表明了该指针是否引用了sidetable 散列表,之所以有这个选项,是因为少量的引用计数是不会直接存放在SideTables表中的,对象的引用计数会存放在extra_rc 中,当其被存满时,才会存入相应的SideTables 散列表中,SideTable 中有很多张SideTable,每个SideTable 也都是一个散列表,而引用计数表就包含在SideTable之中。
散列表 (引用计数表,弱引用表)
引用计数要么存放在isa 的 extra_rc 中,要么存放在引用计数表中,而引用计数表中包含在一个叫SideTable 的结构中,它是一个散列表,也就是哈希表。而SideTable又包含在一个全局的StripeMap的哈希映射表中,这个表的名字叫SideTables。
当一个对象访问SideTables 时:
1、首先会取得对象的地址,将地址进行哈希运算,与SideTables中的SideTabel的个数取余,最后得到的结果就是该对象所访问的SideTable
2、在取的SideTable 中RefcountMap 表中再进行一次哈希查找。找到该对象在引用计数表中对应的位置。
3、如果该对象存在对应的引用计数,则对其进行操作,如果没有对应的引用计数,则创建一个对应的size_t对象,其实就是一个uint类型的无符号整型。
弱引用表也是一张哈希表的结构,其内部包含了每个对象对应的弱引用表weak_entry_t,而weak_entry_t 是一个结构数组,其中包含的则是每一个对象弱引用的对象对应的弱引用指针。