在本章中,我们从NSObject的定义出发,了解了OC中类和对象所对应的数据结构objc_class和objc_object。关于NSObject,objc_class和objc_object三者之间的关系,我们可以用下面的图来更清晰的了解:
其实 isa_t 是一个定义得非常"奇怪"的结构体,在 ObjC 源代码中可以看到这样的定义:
#define ISA_MASK 0x00007ffffffffff8ULL
#define ISA_MAGIC_MASK 0x001f800000000001ULL
#define ISA_MAGIC_VALUE 0x001d800000000001ULL
#define RC_ONE (1ULL<<56)
#define RC_HALF (1ULL<<7)
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
struct {
uintptr_t indexed : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 44;
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 8;
};
};
这是在 __x86_64__ 上的实现,对于 iPhone5s 等架构为 __arm64__ 的设备上,具体结构体的实现和位数可能有些差别,不过这些字段都是存在的,可以看这里的arm64 上结构体的实现。
在本篇文章中, 我们会以 __x86_64__ 为例进行分析,而不会对两种架构下由于不同的内存布局方式导致的差异进行分析。在我看来,这个细节不会影响对 isa 指针的理解,不过还是要知道的。
笔者对这个 isa_t 的实现声明顺序有一些更改,更方便分析和理解。
union isa_t {
...
};
isa_t 是一个 union 类型的结构体,对 union 不熟悉的读者可以看这个 stackoverflow 上的回答。也就是说其中的 isa_t、cls、 bits 还有结构体共用同一块地址空间。而 isa 总共会占据 64 位的内存空间(决定于其中的结构体)
struct {
uintptr_t indexed : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 44;
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 8;
};
在 isa_t 中,我们还有一些没有介绍的其它 bits,在这个小结就简单介绍下这些 bits 的作用
has_assoc:对象含有或者曾经含有关联引用,没有关联引用的可以更快地释放内存
weakly_referenced:对象被指向或者曾经指向一个 ARC 的弱变量,没有弱引用的对象可以更快释放
deallocating:对象正在释放内存
has_sidetable_rc:对象的引用计数太大了,存不下
extra_rc:对象的引用计数超过 1,会存在这个这个里面,如果引用计数为 10,extra_rc 的值就为 9
从 NSObject 的初始化了解 isa