OC的每个对象中都会有一个isa指针,每个类中都会有一个superclass指针,那么它们有什么用?实例对象,类对象,元类对象之间又有什么联系呢?
isa指针
- 对象的isa 指向哪里?
首先我们准备一个继承自NSObject的JJPerson类,定义一个对象方法和一个类方法,然后我们分别调用这两个方法。

类方法实际在元类对象中而不是类中,对象方法实际存放在类对象中而不是实例对象中,那它们分别是怎么调用到并没有存放在自身的东西呢?这时候就是isa指针发挥作用了。

如上图所示:
- instance的isa指向class
- 当调用对象方法时,通过instance的isa找到class,最后找到对象方法的实现进行调用
- class的isa指向meta-class
- 当调用类方法时;通过class的isa找到meta-ciass,最后找到类方法的实现进行调用
class对象的superclass指针
我们把问题变得稍微复杂一些,我们为JJPerson增加一个子类JJStudent,同样顺手为它准备一个对象方法和一个类方法。

正如我们所知,student继承自person,它是可以调用父类的方法的。

但是Person的对象方法和类方法都是存放在Person的类对象和元类对象中,Student的类和实例对象是怎么调用它们的呢?这样的情况下isa指针看起来已经不能满足我们的需求了,那这时候就需要superclass指针。

- 当Student的instance对象要调用Person的对象方法时,会先通过isa找到Student的class,然后通过superclass找到Person的class,最后找到对象方法的实现进行调用

- 当Student的class要调用Person的类方法时,会先通过isa找到Student的meta-class,然后通过superclass找到Person的meta-class,最后找到类方法的实现进行调用
isa和superclass总结
这张经典的图相信很多iOS开发者都看过,它很好地指出了isa和superclass在OC对象之间所担任的联系

综合我们上面提到的例子,我们可以为这张图加上备注方便理解

总结如下:
-
instance的isa指向class
-
class的isa指向meta-class
-
meta-class的isa指向基类的meta-class
-
class的superclass指向父类的class
-
如果没有父类,superclass指针为nil
-
meta-class的superclass指向父类的meta-class
-
基类的meta-class的superclass指向基类的class
-
instance调用对象方法的轨迹
- isa找到class,方法不存在,就通过superclass找父类
-
class调用类方法的轨迹
- isa找meta-class,方法不存在,就通过superclass找父类
isa细节
前面我们提到,在OC对象中实例对象的isa是指向类对象,类对象的isa指向元类对象,这样我们可以通过isa把OC中的三种对象联系起来。那么,isa是怎么实现的呢?实例对象的isa直接存放着类对象的地址,类对象的isa直接存放元类对象的地址吗?
- 首先我们准备一个JJPerson继承自NSObject,分别拿到它的实例对象,类对象和元类对象,并分别打印它们的地址值
- 我们先通过打印拿到person的isa地址和person的类对象地址,通过对比我们发现,其实他们是并不相等的
- person->isa:0x001d8001000011d1
- personClass:0x00000001000011d0

在控制台可以通过以下命令来查看对象的isa指针:
(1ldb) p person–>isa
(Class) $0 = MJPerson
如果想要看isa的地址值可以通过强转的方式:
(1ldb) p (long)person->isa
(1ong) S1 = 8303516187936969
这样输出的地址值是十进制的,通过下面命令可以输出十六进制的地址值:
(11db) p/x (long)person->isa
(1ong) S2 = 8x801d8001000014c9
那么实例对象是怎么通过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进行一次位运算才能得出真实地址。
- 以前对象的isa指针是直接指向类对象的,但是从64bit开始,isa需要进行一次位运算,才能计算出真实地址,位运算是& ISA_MASK


- 子类的类对象super class指针是直接指向父类的类对象的,不存在& ISA_MASK
网友评论