[关闭]
@qidiandasheng 2020-10-21T11:12:45.000000Z 字数 4480 阅读 703

关于isa

iOS运行时


介绍

ARM64位架构之前,isa是一个指针,指向class/meta-class对象的地址
ARM64位架构开始,isa是一个联合体/共用体(union),这是苹果对isa的优化,结合位域的概念以及位运算的方式来存储更多类相关信息,简单来说就是isa指针通过一个叫ISA_MASK的值进行二进制&运算,得到真实的class/meta-class对象的真实地址

64位架构之前:

  1. /// An opaque type that represents an Objective-C class.
  2. typedef struct objc_class *Class;
  3. /// Represents an instance of a class.
  4. struct objc_object {
  5. Class _Nonnull isa OBJC_ISA_AVAILABILITY;
  6. };

64位架构之后:

  1. struct objc_object {
  2. private:
  3. isa_t isa;
  4. public:
  5. // ISA() assumes this is NOT a tagged pointer object
  6. Class ISA();
  7. // rawISA() assumes this is NOT a tagged pointer object or a non pointer ISA
  8. Class rawISA();
  9. .
  10. .
  11. .
  12. void initIsa(Class cls /*nonpointer=false*/);
  13. void initClassIsa(Class cls /*nonpointer=maybe*/);
  14. void initProtocolIsa(Class cls /*nonpointer=maybe*/);
  15. void initInstanceIsa(Class cls, bool hasCxxDtor);
  16. .
  17. .
  18. .
  19. .
  20. }

isa_t类型

64位架构开始,不再是一个普通的isa指针了,而是isa_t,这个东西进一步查看发现

  1. union isa_t {
  2. isa_t() { }
  3. isa_t(uintptr_t value) : bits(value) { }
  4. Class cls;
  5. uintptr_t bits;
  6. #if defined(ISA_BITFIELD)
  7. struct {
  8. ISA_BITFIELD; // defined in isa.h
  9. };
  10. #endif
  11. };
  1. # if __arm64__
  2. # define ISA_MASK 0x0000000ffffffff8ULL
  3. # define ISA_MAGIC_MASK 0x000003f000000001ULL
  4. # define ISA_MAGIC_VALUE 0x000001a000000001ULL
  5. # define ISA_BITFIELD \
  6. uintptr_t nonpointer : 1; \
  7. uintptr_t has_assoc : 1; \
  8. uintptr_t has_cxx_dtor : 1; \
  9. uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
  10. uintptr_t magic : 6; \
  11. uintptr_t weakly_referenced : 1; \
  12. uintptr_t deallocating : 1; \
  13. uintptr_t has_sidetable_rc : 1; \
  14. uintptr_t extra_rc : 19
  15. # define RC_ONE (1ULL<<45)
  16. # define RC_HALF (1ULL<<18)
  17. # elif __x86_64__
  18. # define ISA_MASK 0x00007ffffffffff8ULL
  19. # define ISA_MAGIC_MASK 0x001f800000000001ULL
  20. # define ISA_MAGIC_VALUE 0x001d800000000001ULL
  21. # define ISA_BITFIELD \
  22. uintptr_t nonpointer : 1; \
  23. uintptr_t has_assoc : 1; \
  24. uintptr_t has_cxx_dtor : 1; \
  25. uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
  26. uintptr_t magic : 6; \
  27. uintptr_t weakly_referenced : 1; \
  28. uintptr_t deallocating : 1; \
  29. uintptr_t has_sidetable_rc : 1; \
  30. uintptr_t extra_rc : 8
  31. # define RC_ONE (1ULL<<56)
  32. # define RC_HALF (1ULL<<7)
  33. # else
  34. # error unknown architecture for packed isa
  35. # endif
  36. // SUPPORT_PACKED_ISA
  37. #endif

ISA_BITFIELDx86_64arm64中拥有不同的值,我们可以从下图看出他们的区别:

23_8.png-80.1kB

什么是union

union的定义是它使几个不同类型的变量共占一段内存(相互覆盖),每次只有一个能使用。 也就是说其中的 isa_tclsbits 还有结构体共用同一块地址空间。而 isa 总共会占据 64 位的内存空间(决定于其中的结构体)

objc-isa-isat.png-74.9kB

nonpointer

0 表示 raw isa,也就是没有结构体的部分,访问对象的 isa 会直接返回一个指向 cls 的指针,也就是64 位系统之前时 isa 的类型:

  1. union isa_t {
  2. isa_t() { }
  3. isa_t(uintptr_t value) : bits(value) { }
  4. Class cls;
  5. uintptr_t bits;
  6. };

1 表示当前 isa 不是指针,但是其中也有 cls 的信息,只是其中关于类的指针都是保存在 shiftcls 中:

  1. union isa_t {
  2. isa_t() { }
  3. isa_t(uintptr_t value) : bits(value) { }
  4. Class cls;
  5. uintptr_t bits;
  6. struct {
  7. uintptr_t nonpointer : 1;
  8. uintptr_t has_assoc : 1;
  9. uintptr_t has_cxx_dtor : 1;
  10. uintptr_t shiftcls : 44;
  11. uintptr_t magic : 6;
  12. uintptr_t weakly_referenced : 1;
  13. uintptr_t deallocating : 1;
  14. uintptr_t has_sidetable_rc : 1;
  15. uintptr_t extra_rc : 8;
  16. };
  17. };

has_assoc

对象含有或者曾经含有关联引用,没有关联引用的可以更快地释放内存

has_cxx_dtor

表示该对象是否有 C++ 或者 Objc 的析构器

shiftcls

类的指针。arm64架构中有33位可以存储类指针。

magic

判断对象是否初始化完成,在arm64中0x16是调试器判断当前对象是真的对象还是没有初始化的空间。

weakly_referenced

对象被指向或者曾经指向一个 ARC 的弱变量,没有弱引用的对象可以更快释放

deallocating

对象是否正在释放内存

has_sidetable_rc

判断该对象的引用计数是否过大,如果过大则需要其他散列表来进行存储。

extra_rc

存放该对象的引用计数值减一后的结果。对象的引用计数超过 1,会存在这个这个里面,如果引用计数为 10,extra_rc的值就为 9。

初始化initIsa()

注:以下以x86_64为例

  1. inline void
  2. objc_object::initIsa(Class cls)
  3. {
  4. initIsa(cls, false, false);
  5. }
  1. //这里对方法做了简化,留下重要的部分
  2. inline void
  3. objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
  4. {
  5. isa.bits = ISA_MAGIC_VALUE;
  6. isa.has_cxx_dtor = hasCxxDtor;
  7. isa.shiftcls = (uintptr_t)cls >> 3;
  8. }

第一个参数nonpointer表示是否开启指针优化。nonpointer=true,表示开启。

这就是Tagged Pointer技术的一种使用。在64位系统中,一个指针的大小将是64位(8字节)。使用这种技术之后我们可以在一个指针中存储一些信息,节省内存空间。

设置bits

对整个 isa 的值 bits 进行设置,传入 ISA_MAGIC_VALUE

#define ISA_MAGIC_VALUE 0x001d800000000001ULL

我们可以把它转换成二进制的数据11101100000000000000000000000000000000000000000000001(一共53位),然后看一下哪些属性对应的位被这行代码初始化了(标记为红色):

objc-isa-isat-bits.png-91kB

从图中了解到,在使用 ISA_MAGIC_VALUE 设置 isa_t 结构体之后,实际上只是设置了 nonpointer 以及 magic 这两部分的值。

设置has_cxx_dtor

在设置 nonpointermagic 值之后,会设置 isa 的 has_cxx_dtor,这一位表示当前对象有 C++ 或者 ObjC 的析构器(destructor),如果没有析构器就会快速释放内存。

isa.has_cxx_dtor = hasCxxDtor;

objc-isa-isat-bits-has-css-dtor.png-90.8kB

设置shiftcls

isa.shiftcls = (uintptr_t)cls >> 3;

将当前地址右移三位的主要原因是用于将 Class 指针中无用的后三位清除减小内存的消耗,因为类的指针要按照字节(8 bits)对齐内存,其指针后三位都是没有意义的 0。

objc-isa-isat-class-highlight-bits.png-91kB

获取isa类指针

uintptr_t bits;这个东西类型是unsigned long类型,表达的是类的信息。苹果结合了位域的概念和位运算来做的优化,也就是将结构体中的成员通过位域这种数据结构分配了各个成员的内存,达到节约内存的目的,再通过各种MASK跟bits进行&运算,得到想要的信息,具体想要什么信息就拿对应的MASK去运算。

# define ISA_MASK 0x00007ffffffffff8ULL

十六进制转换为二进制为11111111111111111111111111111111111111111111000(47位),通过bitsISA_MASK位与运算得到isa类指针,也就是说除去后面3个0,和bits的第4位开始的44个数与运算得到indexcls

  1. inline Class
  2. objc_object::ISA()
  3. {
  4. //非TaggedPointer类型继续往下走
  5. ASSERT(!isTaggedPointer());
  6. #if SUPPORT_INDEXED_ISA
  7. if (isa.nonpointer) {
  8. uintptr_t slot = isa.indexcls;
  9. return classForIndex((unsigned)slot);
  10. }
  11. return (Class)isa.bits;
  12. #else
  13. return (Class)(isa.bits & ISA_MASK);
  14. #endif
  15. }

参考

Runtime入院第一天—— isa 和 Class
iOS---关于isa
从 NSObject 的初始化了解 isa

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注