在 Objective-C 运行时现在的实现中,objc_objcet
的结构体是这样定义的
struct objc_object {
private:
isa_t isa;
};
isa
从 Class
变成了 isa_t
,那这个 isa_t
是个什么东西呢?它的定义在 x86-64 上是这样的
union isa_t
{
Class cls;
uintptr_t bits;
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 8;
};
};
uintptr_t
是一个和指针一样长的无符号整形,方便对指针进行算术运算。
知道联合的都清楚,cls
、bits
和最后的匿名结构体是占用同一片内存的。这是 Objective-C 运行时对 64 位的优化,我们知道指针的长度是和 CPU 有关系,在 64 位下它就有 64 位长,如果和之前一样,整个指针都用来存一个地址,是比较浪费的,甚至虚拟内存根本都没那么大。所以可以让指针中一部分位拿来做别的用途,即所谓的 Tagged Pointer。
可以参考这篇文章知道这些位的用途:
- 1 bit
nonpointer
用来标示是否「不是单纯的指针」 - 1 bit
has_assoc
用来标示是否有关联对象 - 1 bit
has_cxx_dtor
用来标示是否有 C++ destructor - 44 bit
shiftcls
右移(shift)了 3 位(8 byte 对齐,最后 3 位总是 0)的Class
(cls)地址 - 6 bit
magic
给 debugger 区分对象和垃圾内存的值 - 1 bit
weakly_referenced
是否被弱引用过 - 1 bit
deallocating
是否正在销毁 - 1 bit
has_sidetable_rc
是否有在别的地方存引用计数(引用计数太大,extra_rc
存不下了) - 8 bit
extra_rc
引用计数超过 1 的部分(如果这里是 5,那对象的引用计数就是 6)
总结
这些位都是给一些不同功能的优化。像对消息发送来说,只要能获取到 Class
,它对这个实际结构是不关心的。所以之前消息发送的文章中,不知道这个结构对理解原理也是没有什么关系的,只是在实现细节部分会有所不同。这些不同位的使用细节,也是要通过之后不同具体功能的文章来具体分析了。