本文使用的 runtime 版本为 objc4-706

在 Objective-C 运行时现在的实现中,objc_objcet 的结构体是这样定义的:

struct objc_object {
private:
    isa_t isa;
};

isaClass 变成了 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 是一个和指针一样长的无符号整形,方便对指针进行算术运算。

知道联合的都清楚,clsbits 和最后的匿名结构体是占用同一片内存的。这是 Objective-C 运行时对 64 位的优化,我们知道指针的长度是和 CPU 有关系,在 64 位下它就有 64 位长,如果和之前一样,整个指针都用来存一个地址,是比较浪费的,甚至虚拟内存根本都没那么大。所以可以让指针中一部分位拿来做别的用途,即所谓的 tagged pointer

可以参考这篇文章知道这些位的用途:

1 bit nonpointer 用来标示是否「不是单纯的指针」
1 bit has_assoc 用来标示是否有关联对象
1 bit has_cxx_dtor 用来标示是否有 C++ destructor
44 bits shiftcls 右移(shift)了 3 位(8 byte 对齐,最后 3 位总是 0)的 Class(cls)地址
6 bits magic 给 debugger 区分对象和垃圾内存的值
1 bit weakly_referenced 是否被弱引用过
1 bit deallocating 是否正在销毁
1 bit has_sidetable_rc 是否有在别的地方存引用计数(引用计数太大,extra_rc 存不下了)
8 bits extra_rc 引用计数超过 1 的部分(如果这里是 5,那对象的引用计数就是 6)

总结

这些位都是给一些不同功能的优化。像对消息发送来说,只要能获取到 Class,它对这个实际结构是不关心的。所以之前消息发送的文章中,不知道这个结构对理解原理也是没有什么关系的,只是在实现细节部分会有所不同。这些不同位的使用细节,也是要通过之后不同具体功能的文章来具体分析了。