iOS底层原理02-objc对象的底层探索(下)

260 阅读5分钟

上篇 objc对象的底层探索(上)说到,对象的本质是一个objc_object的结构体,其结构体内存储的是 isa指针 + 成员变量的值。本篇来着重说明一下isa指针

结构体与联合体

在isa指针前,需要补充两个概念,结构体与联合体。

构造数据类型的方式有以下两种:

结构体struct

联合体union,也称为共用体

结构体

结构体是指把不同的数据组合成一个整体,其变量共存的,变量不管是否使用,都会分配内存。结构体的各个成员会占用不同的内存,互相之间没有影响

  • 优点:存储容量较大包容性强,且成员之间不会相互影响(占用不同内存) 。

  • 缺点:结构体内存 >= 所有成员占用的内存总和。所有属性都分配内存,比较浪费内存。假设有4个int成员,一共分配了16字节的内存,但是在使用时,你只使用了4字节,剩余的12字节就是属于内存的浪费

联合体

联合体也是由不同的数据类型组成,但其变量是互斥的,所有的成员共占一段内存。而且共用体采用了内存覆盖技术同一时刻只能保存一个成员的值,一次只能使用一个成员,如果对新的成员赋值,就会将原来成员的值覆盖掉,联合体中可以定义多个成员,联合体的大小由最大的成员大小和基础数据类型的整数倍决定,共用体占用的内存等于基础数据类型的整数倍且能覆盖到最大的成员占用的内存

  • 优点:所有成员共用一段内存,修改一个成员会影响其余所有成员。 使内存的使用更为精细灵活,同时也节省了内存空间,存储效率更高

  • 缺点:每个变量是互斥的,且包容性差

联合体代码定义如下

image.png

可以看到,分别打印三次赋值的输出,只有当前成员变量赋值生效。

image.png

再看一下大小的计算,按基础数据整数倍(8的倍数)且容纳最大成员变量占用内存(11),所以该结构体大小为16

isa的结构

上篇(见顶部)中知道,_class_createInstanceFromZone中会调用initIsa方法

image.png image.png

方法中可以看到一个isa_t,原来isa_t是一个联合体,看下来重点就应该在ISA_BITFIELD

image.png

使用__x86_64__举例

参数名作用大小所在位置
nonpointer是否对isa指针开启指针优化0:纯isa指针只包含类对象地址1:isa中包含了类对象地址、类信息、对象的引用计数等10
has_assoc是否有关联对象0:没有1:存在11
has_cxx_dtor该对象是否有C++或者Objc的析构器 如果有析构函数则需要做析构逻辑如果没有则可以更快的释放对象12
shiftcls存储类指针的值。开启指针优化的情况下,在arm64架构中有 44 位用来存储类指针443~46
magic用于调试器判断当前对象是真的对象还是没有初始化的空间647~52
weakly_referenced是否有弱引用0:没有1:存在153
unused是否正在释放内存0:不是1:是154
has_sidetable_rc是否需要用到外挂引用计数,当对象引用技术大于 10 则需要借用该变量存储进位155
extra_rc该对象的引用计数值,实际上是引用计数值减 1。 如果对象的引用计数为10,那么 extra_rc 为 9。如果引用计数大于 10 则需要使用 has_sidetable_rc856~63

那么我们知道了,在开启isa优化的时候对象的指针是存在isashiftcls里面,那么怎么获得shiftcls呢?我们强转isa的类型为isa_t后打印

image.png

isa类获取

根据isa指针内存分配位置可知,只需要将isa中的地址shiftcls右移3位即可获取到类,见上图(使用__x86_64__举例)

image.png

怎么从对象获取类

平常获取对象的类会直接调用class方法,那么class方法内部实现是怎样的?

image.png

image.png

通过方法一层一层找下来,会找到一个掩码ISA_MASK

image.png

image.png

那我们是不是可以通过这个掩码来获取一下类呢,我们来实验一下

image.png

isa指向探索

这个时候已经验证了可以通过对象的isa获取到对象的类,那么类本身也是一个对象,它的isa又是指向那里呢?

image.png

  • 简单的多打印几层,我们看到打印结果有3层是LGPerson,第一层是我们的对象,第二层是类对象。第三层虽然也是LGPerson,但是地址和第二层的不一样,说明不是同一个对象。类对象在内存中是只能存在一个的,那么第三层肯定就是元类了。

  • 往上继续打印,发现元类对象isa指向的是NSObjectNSObject的象isa指向的也是NSObject。看看2个NSObject的地址是相同的,所以NSObjectisa是指向了自身,也就是NSObjectLGPerson具有同一个根元类。

那么我们可以得到四个结论

  • 对象的isa指向其类
  • 类对象的isa指向其元类
  • 元类的isa指向根元类
  • 所有元类的isa都指向根元类
  • 所有根元类的isa都指向它自己

image.png