引言
001-OC对象原理探究 - alloc这篇文章的探索过程中,大体上讲述了alloc的基本流程。其中探索到_class_createInstanceFromZone方法中的下面这段代码时,简单讲了对象的关联。

  1. if (!zone && fast) {
  2. obj->initInstanceIsa(cls, hasCxxDtor);
  3. } else {
  4. obj->initIsa(cls);
  5. }

因此,本文将详细探究这个神秘的isa
###initInstanceIsainitIsa源码
我们在源码中,点击initInstanceIsa(cls,hasCxxDtor)进入到objc-object.h中可以看到,函数内部做了两个断言

  1. ASSERT(!cls->instancesRequireRawIsa());
  2. ASSERT(hasCxxDtor == cls->hasCxxDtor());

1、instancesRequireRawIsa()内部为cache.getBit(FAST_CACHE_REQUIRES_RAW_ISA);。其中FAST_CACHE_REQUIRES_RAW_ISA为类的cache_t中的uint16_t _flags第13位是否为1,以此来表示类对象(非实例对象)是否需要原始的isa。

2、cls->hasCxxDtor()内部为cache.getBit(FAST_CACHE_HAS_CXX_DTOR);。其中FAST_CACHE_HAS_CXX_DTOR为类cache_tuint16_t _flags第2位是否为1,以此来表示当前类或者父类是否有c++的析构函数的实现。

接下来就是进入到initIsa(cls, true, hasCxxDtor);内部源码如下:

  1. inline void
  2. objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
  3. {
  4. ASSERT(!isTaggedPointer());
  5. isa_t newisa(0);
  6. if (!nonpointer) {
  7. newisa.setClass(cls, this);
  8. } else {
  9. ASSERT(!DisableNonpointerIsa);
  10. ASSERT(!cls->instancesRequireRawIsa());
  11. #if SUPPORT_INDEXED_ISA
  12. ASSERT(cls->classArrayIndex() > 0);
  13. newisa.bits = ISA_INDEX_MAGIC_VALUE;
  14. newisa.has_cxx_dtor = hasCxxDtor;
  15. newisa.indexcls = (uintptr_t)cls->classArrayIndex();
  16. #else
  17. newisa.bits = ISA_MAGIC_VALUE;
  18. # if ISA_HAS_CXX_DTOR_BIT
  19. newisa.has_cxx_dtor = hasCxxDtor;
  20. # endif
  21. newisa.setClass(cls, this);
  22. #endif
  23. newisa.extra_rc = 1;
  24. }
  25. isa = newisa;
  26. }

1、由initInstanceIsa调用的initIsanonpointer传的值为true,走的是else内部逻辑,对newisa多项赋值。
2、由obj->initIsa(cls);调用的initIsanonpointer传的值为false,走的是if内部逻辑,只对newisa的class设置即可。
3、在initIsa方法中,首先第一句就断言ASSERT(!isTaggedPointer());是否为TaggedPointer,如果是TaggedPointer则无法继续后续的逻辑,即无isa

isa的内部结构以及nonpointerTaggedPointer的解释
isa结构源码如下,是一个联合体+位域的结构:
联合体union内部成员为互斥存在,即联合体所占内存大小决定于内部最大成员所占大小。结构体struct则是“有容乃大”,占内存大小可参考002-OC对象原理探究 - 结构体内存对齐

  1. union isa_t {
  2. isa_t() { } // 构造方法
  3. isa_t(uintptr_t value) : bits(value) { } // 构造方法
  4. uintptr_t bits;
  5. private:
  6. Class cls;
  7. public:
  8. #if defined(ISA_BITFIELD)
  9. struct {
  10. ISA_BITFIELD; // 此宏为isa内部位域的分配情况,源码在下面
  11. };
  12. bool isDeallocating() {
  13. return extra_rc == 0 && has_sidetable_rc == 0;
  14. }
  15. void setDeallocating() {
  16. extra_rc = 0;
  17. has_sidetable_rc = 0;
  18. }
  19. #endif
  20. void setClass(Class cls, objc_object *obj);
  21. Class getClass(bool authenticated);
  22. Class getDecodedClass(bool authenticated);
  23. };

其中ISA_BITFIELD此宏为isa内部位域的分配情况:(:1:44等分别表示该成员占1位44位

  1. # if __arm64__
  2. uintptr_t nonpointer : 1;
  3. uintptr_t has_assoc : 1;
  4. uintptr_t has_cxx_dtor : 1;
  5. uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/
  6. uintptr_t magic : 6;
  7. uintptr_t weakly_referenced : 1;
  8. uintptr_t unused : 1;
  9. uintptr_t has_sidetable_rc : 1;
  10. uintptr_t extra_rc : 19
  11. # elif __x86_64__
  12. uintptr_t nonpointer : 1;
  13. uintptr_t has_assoc : 1;
  14. uintptr_t has_cxx_dtor : 1;
  15. uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/
  16. uintptr_t magic : 6;
  17. uintptr_t weakly_referenced : 1;
  18. uintptr_t unused : 1;
  19. uintptr_t has_sidetable_rc : 1;
  20. uintptr_t extra_rc : 8

-nonpointer: 是否开启NONPOINTER isa指针优化;
-has_assoc: 对象是否含有关联引用
-has_cxx_dtor:对象是否含有 C++ 或者 Objc 的析构器
-shiftcls: 类的指针(重点)(arm64:33位,x86_64:44位)
-magic: 对象是否初始化完成 (arm64:0x16 ,x86_64:0x3b)
-weakly_referenced:是否为弱引用的对象
-deallocating:对象是否正在执行析构函数(是否在释放内存)
-has_sidetable_rc:判断是否需要用sidetable去处理引用计数,(extra_rc的大小影响到这个变量)
-extra_rc: 存储该对象的引用计数值减一后的结果
isa的二进制分布如图所示:
003-OC对象原理探究 - isa 和 nonpointer - 图1

TaggedPointer
早期64位架构中,存储基础数据类型 , 底层会封装成 NSNumber , 在开辟8字节内存,32位架构开辟4字节。维护引用计数,管理生命期 。造成运行效率上的损失 。会造成很大空间浪费。因此,引入TaggedPointer。当断言为TaggedPointer,则对象指针的值不是地址了,而是真正的值,直接优化了内存,提升了获取速度。TaggedPointer的更多知识,可阅读这篇文章
####nonpointer
ISA_BITFIELD内部,可看到nonpointer占1位,表示是否对 isa 指针开启指针优化(0:纯isa指针,1:不⽌是类对象地址,isa 中包含了类信息、对象的引⽤计数等)。应用判断,在上面已说明。

isa的用法
isa的用法和isa的走向将在下篇文章探究。
###总结
每个类都有一个isa,isa的探究重要性不言而喻。我们通过alloc流程探究到isa的内部结构,补上了001文章的alloc流程的一个坑。好累,拜拜。。