代替 isa 指针的是结构体 isa_t
当实例方法被调用时,它要通过自己持有的isa 来查找对应的类,然后在object_class的 class_data_bits_t中查找对应方法的实现
objc-private.h 能翻出其定义
对于不支持优化的,isa_t共用体,我们用到的只有cls,也就是类指针,对于支持优化的,例如__x86_64__ ,iPhone5s等架构为 __arm64__,我们使用的是结构体,具体结构体的实现和位数可能有些差别,不过这些字段都是存在的。
我们可以通过 isa 初始化的方法 initIsa 来初步了解这 64 位的 bits 的作用:
inline void objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
initIsa(cls, true, hasCxxDtor);
}
inline void objc_object::initIsa(Class cls, bool indexed, bool hasCxxDtor)
{
if (!indexed) {
isa.cls = cls;
} else {
isa.bits = ISA_MAGIC_VALUE;
isa.has_cxx_dtor = hasCxxDtor;
isa.shiftcls = (uintptr_t)cls >> 3;
}
}
由于在 initInstanceIsa 方法中传入了 indexed = true,所以,我们简化一下这个函数的实现:
inline void objc_object::initIsa(Class cls, bool indexed, bool hasCxxDtor)
{
isa.bits = ISA_MAGIC_VALUE;
isa.has_cxx_dtor = hasCxxDtor;
isa.shiftcls = (uintptr_t)cls >> 3;
}
#define ISA_MAGIC_VALUE 0x001d800000000001ULL
由于共用体的特性,对bits赋值,等价于对结构体赋值。0x0000000000000001ULL 64位二进制数,放入共用体变量空间。
我感觉这个uintptr_t,于大小端模式是有关系的,这也是疑惑。假设是,小端模式。
又因为,结构体首成员在低地址,尾成员在高地址。
在使用 ISA_MAGIC_VALUE 设置 isa_t 结构体之后,实际上只是设置了 indexed 以及 magic 这两部分的值。结构体其余成员均为0。(indexed= 1)
其中 indexed 表示 isa_t 的类型:
0 表示 raw isa,也就是没有结构体的部分,访问对象的 isa 会直接返回一个指向 cls 的指针,也就是在 iPhone 迁移到 64 位系统之前时 isa 的类型
1 表示当前 isa 不是指针,但是其中也有 cls 的信息,只是其中关于类的指针都是保存在 shiftcls 中。
magic 用于调试器判断当前对象是真的对象还是没有初始化的空间
isa.has_cxx_dtor = hasCxxDtor;
在设置 indexed 和 magic 值之后,会设置 isa 的 has_cxx_dtor,这一位表示当前对象有 C++ 或者 ObjC 的析构器(destructor),如果没有析构器就会快速释放内存。
isa.shiftcls = (uintptr_t)cls >> 3;
在为 indexed、 magic 和 has_cxx_dtor 设置之后,我们就要将当前对象对应的类指针存入 isa 结构体中了。
使用整个指针大小的内存来存储 类地址(cls)有些浪费,尤其在 64 位的 CPU 上。在 ARM 64 运行的 iOS 只使用了 33 位作为指针(与结构体中的 33 位无关,Mac OS 上为 47 位),而剩下的 31 位用于其它目的。类的指针也同样根据字节对齐了,每一个类指针的地址都能够被 8 整除,也就是使最后 3 bits 为 0,为 isa 留下 34 位用于性能的优化。苹果的ARM64运行时利用了一些很棒的性能改进
这一段话是什么意思呢?
64位CPU地址总线通常64根,表示的二进制数64位,也就是一个地址8个字节。无论是类地址,对象地址。但是呢,这64个字节并不是全部用到,没用到的高位补0,我们看以十六进制打印的类地址,有12*4=48,有9*4=36,我们肯定取48,但是48位的,最高位十六进制均为7,转为二进制位0111,也就是说,64位的有效位只有48-1=47位,其余高位补0。而ARM64上,类的地址有效位只有33位。
而且,地址是要内存对齐的,如64位八字节对齐(地址都能够被8整除),因此,这些有效位中,最低三位必然为0
因此,在isa中存储Class指针时右移三位是没有问题的。
我搜索到了一篇文章,是arm64 and you的译文 中有这么一段话:
尽管指针为64位,但在实际使用中,这些位数并不是都用上了。例如X86-64的Mac OS X系统仅使用了其中的47位。而ARM64上占用得更少,目前只有33位。只要未被系统全部占用,这些指针就能用于存储数据。这是Objective-C Runtime演进史上最重要的变化之一。
Objective-C对象是连续的内存块,这个内存块中第一个指针大小的部分称为ISA。一般来说,ISA是一个指向该对象所属类的指针。不过这么大的空间仅作为指针有点儿浪费,尤其是在64位CPU上。运行iOS的ARM64目前仅使用了一个指针的33位,而其余31位则另作他用。另外,类 指针还需要对齐,这就释放了另外3位,于是ISA指针中共有34位可另作他用。苹果的ARM64 Runtime正是利用了这一点使性能有了大幅提升。
网友评论