题记
前面我们提到,在OC对象中实例对象的isa是指向类对象,类对象的isa指向元类对象,这样我们可以通过isa把OC中的三种对象联系起来。那么,isa是怎么实现的呢?实例对象的isa直接存放着类对象的地址,类对象的isa直接存放元类对象的地址吗?
准备工作
- 首先老规矩我们准备一个JJPerson继承自NSObject,分别拿到它的实例对象,类对象和元类对象,并分别打印它们的地址值
- 我们先通过打印拿到person的isa地址和person的类对象地址,通过对比我们发现,其实他们是并不相等的
- person->isa:0x001d8001000011d1
- personClass:0x00000001000011d0
那么实例对象是怎么通过isa来找到类对象的呢?这里就不得不提一个ISA_MASK的东西,我们还是从源码寻找答案。
分析
- 其实从64bit开始,isa需要进行一次位运算,才能计算出真实地址,这个位运算的对象就是ISA_MASK
-
ISA_MASK的值不是唯一的,在arm64和X86架构下是不同的
有了ISA_MASK后我们可以看到看到,person的isa通过和ISA_MASK进行一次位运算后,得出的值就是我们JJPerson类对象在内存中的值。同理类对象和元类对象我们也可以去证明。不过我们直接尝试去获取类对象的isa时,会发现并不能直接获取到,因为Xcode提示说它并不认为这是一个结构体
但是我们点击Class进去,再点击进入它的objc_class里面可以看到,它是确实存在isa的,但是只是没有暴露给我们而已
其实这个问题不难解决,因为我们可以看到objc_class的结构,那么我们可以定义一个和它一样的结构体来获取isa
- 为了避免明明冲突,我们定义一个jj_objc_class结构体,和objc_class一样,它里面也有一个Class类型的isa
- 在30行代码处,我们通过自定义的结构体去获取JJPerson的类对象
-
通过打印我们可以发现,我们新获取JJPerson的类对象isa通过和ISA_MASK进行位运算后,得出的地址正是我们JJPerson元类对象的地址
这也证明了,我们OC对象中的isa并不是直接存放所指向对象的地址值,而是需要通过和ISA_MASK进行一次位运算才能得出真实地址。但是为什么苹果要使用这种做法呢?我们后面再来分析。