题记
对于上文提到isa以及superclass的导向图,相信细心的朋友们会发现一个细节,基类的元类对象的superclass指针是指向基类的类对象,我们应该怎么去理解这个指向在代码层面的体现呢?
正文
我们从代码的角度去分析这个问题。首先我们准备一个JJPerson类继承自NSObject,为它增加一个test的类方法。同时我们为NSObject增加一个分类,包括一样名为test的类方法,同时增加打印,可以看到调用对象的地址。我们可以看到,Person调用到Person的test方法,NSObject调用的是自己分类的test方法,这点相信大家是没有疑问的。
那么当我们把Person的test方法的实现注释掉以后再run一次,我们看到NSObject的test方法被调用了2次,这是因为superclass指针的存在,一个类在元类对象查找类方法,如果找不到,就回去根元类对象里面去寻找,如果有,还是可以调用的,这是我们上一篇文章提到的,相信也没有太大问题。
那么高潮来了,我们NSObject分类里面的test类方法注释掉,改成减号开头的test对象方法,我们发现Person和NSObject两个类仍然可以调用到这个对象方法。首先我们知道,把类方法改成对象方法,本质上是把存放在元类对象中的方法存放到了类对象中,按照我们所知,类调用类方法是在元类对象中寻找,但是为什么还能调用到类对象中存放的给实例对象用的对象方法呢?
下面这个图绿色的箭头流程就很好的解释了这个过程。Person的类对象调用test方法,那么首先会到Person的元类对象去寻找,当元类对象没有时,它会到根源类对象里面在寻找,当还没有找到时,就是我们文章开头提出的问题,它会到元类对象里面寻找,当它发现里面存放着名为test的对象方法时,它便可以成功调用。
可能还有朋友会提出,我明明调用的是+开头的方法,为什么最后是-开头的方法被调用呢?这个和我们OC的消息机制有关,无论你是调用什么开头的方法,但是本质上转换成runtime后都是发送一条消息,然后根据方法名称去寻找对应的方法。
objc_msgSend([JJPerson class], @selector(test))
所以,这个就解释了最开始我们的疑问,基类的元类对象的superclass指针是指向基类的类对象。