Objective-C 引用计数原理

462 查看

引用计数如何存储

有些对象如果支持使用 TaggedPointer,苹果会直接将其指针值作为引用计数返回;如果当前设备是 64 位环境并且使用 Objective-C 2.0,那么“一些”对象会使用其 isa 指针的一部分空间来存储它的引用计数;否则 Runtime 会使用一张散列表来管理引用计数。

其实还有一种情况会改变引用计数的存储策略,那就是是否使用垃圾回收(用UseGC属性判断),但这种早已弃用的东西就不要管了,而且初始化垃圾回收机制的 void gc_init(BOOL wantsGC) 方法一直被传入 NO

TaggedPointer

判断当前对象是否在使用 TaggedPointer 是看标志位是否为 1 :

id 其实就是 objc_object * 的简写(typedef struct objc_object *id;),它的 isTaggedPointer() 方法经常会在操作引用计数时用到,因为这决定了存储引用计数的策略。

isa 指针(NONPOINTER_ISA)

用 64 bit 存储一个内存地址显然是种浪费,毕竟很少有那么大内存的设备。于是可以优化存储方案,用一部分额外空间存储其他内容。isa 指针第一位为 1 即表示使用优化的 isa 指针,这里列出不同架构下的 64 位环境中 isa 指针结构:

SUPPORT_NONPOINTER_ISA 用于标记是否支持优化的 isa 指针,其字面含义意思是 isa 的内容不再是类的指针了,而是包含了更多信息,比如引用计数,析构状态,被其他 weak 变量引用情况。判断方法也是根据设备类型:

综合看来目前只有 arm64 架构的设备支持,下面列出了 isa 指针中变量对应的含义:

变量名 含义
indexed 0 表示普通的 Ԩ Objective-C 2.0,那么“一些”对象会使用其 isa 指针的一部分空间来存储它的引用计数;否则 Runtime 会使用一张散列表来管理引用计数。

其实还有一种情况会改变引用计数的存储策略,那就是是否使用垃圾回收(用UseGC属性判断),但这种早已弃用的东西就不要管了,而且初始化垃圾回收机制的 void gc_init(BOOL wantsGC) 方法一直被传入 NO

TaggedPointer

判断当前对象是否在使用 TaggedPointer 是看标志位是否为 1 :

id 其实就是 objc_object * 的简写(typedef struct objc_object *id;),它的 isTaggedPointer() 方法经常会在操作引用计数时用到,因为这决定了存储引用计数的策略。

isa 指针(NONPOINTER_ISA)

用 64 bit 存储一个内存地址显然是种浪费,毕竟很少有那么大内存的设备。于是可以优化存储方案,用一部分额外空间存储其他内容。isa 指针第一位为 1 即表示使用优化的 isa 指针,这里列出不同架构下的 64 位环境中 isa 指针结构:

SUPPORT_NONPOINTER_ISA 用于标记是否支持优化的 isa 指针,其字面含义意思是 isa 的内容不再是类的指针了,而是包含了更多信息,比如引用计数,析构状态,被其他 weak 变量引用情况。判断方法也是根据设备类型:

综合看来目前只有 arm64 架构的设备支持,下面列出了 isa 指针中变量对应的含义:

变量名 含义
indexed 0 表示普通的 uintptr_t indexed : 1; uintptr_t has_assoc : 1; uintptr_t has_cxx_dtor : 1; uintptr_t shiftcls : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000 uintptr_t weakly_referenced : 1; uintptr_t deallocating : 1; uintptr_t has_sidetable_rc : 1; uintptr_t extra_rc : 14; # define RC_ONE (1ULL<<50) # define RC_HALF (1ULL<<13) }; # else // Available bits in isa field are architecture-specific. # error unknown architecture # endif // SUPPORT_NONPOINTER_ISA #endif };
1
2
3
4