在 Objective-C 中,我们都知道对象的释放通过引用计数来控制。通过 Retain 和 Release 来进行引用计数 +1 和 -1。
那引用计数是存储在什么位置,又是在什么时候调用了 delloc 方法?
我们都知道 Objective-C 中的每个对象都是 objc_object 结构体,其中第一个成员变量就是 isa
isa 其实本质上是 union isa_t,下面为 arm64 下的 isa_t
union isa_t
{
Class cls;
uintptr_t bits;
struct {
uintptr_t indexed : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33;
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19;
};
补充: union 在 C++ 中是联合,在联合中的所有成员共享同一个内存,其中 Class 和 bits 和 struct 共享同一块内存也就是 64 bit;在 isa_t 的 struct,其形式称 位域;为什么会有位域这种形式,因为当如果一个结构不需要 8bit 空间大小,只需要 1bit 空间,那么就可以用位域来节约内存空间。在 struct 的 indexed 成员占 1 bit,shiftcls 占 33 bit,依次类推;从 indexed 到 extra_rc 从低到高依次排列
补充: 字段说明
- extra_rc: 64 位下优先保存引用计数的位置
- has_sidetable_rc: 是否有试用 SideTable 保存引用计数
- Indexed: isa 指针是否为 NONPOINTER_ISA
>> 在非 64 位操作系统中,是没有 struct 这个成员的;因为在 64 位系统中为了节约内存,所以引入了 NONPOINTER_ISA,这种 isa 除了指针的功能外还能存储数据。
NONPOINTER_ISA 其中的 extra_rc 就被用来存储引用计数;当 extra_rc 已经存满时, 就会被存在 SideTable 中,has_sidetable_rc 则标记是否存储在 SideTable 中。
64 位系统下的引用计数大致存储原理如下:
- 每次对象调用 Retain 时,extra_rc++,当 extra_rc 正溢出时,抽调 extra_rc 最大值的一半也就是 RC_HALF 到 SideTable 中,此时 has_sidetable_rc 为 true
- 每次对象调用 Release 时,extra_rc--,当 extra_rc 负溢出时,extra_rc 从 SideTable 中借最大值为 RC_HALF 的引用计数;如果 SideTable 中也没有那么 extra_rc 为 0,并调用 dealloc 方法
网友评论