本文由快学吧个人写作,以任何形式转载请表明原文出处。
一、资料准备
对应mac的版本是11.1。可根据自己的系统版本挑选可以进行调试的源码。
二、思路
- 前面一直在说类和分类的加载和实现,加载是load,并且已经看过了,那么实现是什么呢?
initialize的调用时机也比main函数要早,那么initialize是在哪里被执行的?- 类和分类的
initialize是否和load一样,有着执行顺序。
三、探索initialize的方法
1. 代码准备和思路准备 :
在main.m的main函数中初始化JDMan。
然后在JDMan.m中实现+ (void)initialize方法。运行项目,看控制台打印 :
在load方法后,在main函数执行前,initialize方法会进行调用。
然后更改main.m中的JDMan的调用 :
看控制台打印 :
依然是在load方法之后,在main函数执行之前,initialize方法会进行调用。
并且不会因为JDMan从调用alloc变成调用class,就不调用initialize。也就是说,initialize和类调用哪个方法没关系,主要是和类被使用了有关系,或者更精确的说,是和类发送消息,调用方法有关。
所以initialize的实现,应该是在方法调用中,也就是objc_msgSend中,或者在lookUpImpOrForward中。而objc_msgSend是汇编,在汇编中是没有见到initialize的调用的,详情见之前的文章。那么lookUpImpOrForward中是否有之前忽略的点,实现了initialize呢?
2. initialize的实现
进入lookUpImpOrForward中找到有关类的初始化 :
进入源码 :
initializeNonMetaClass源码很多,比较重要的就两点 :
- 初始化类,会先递归循环初始化继承链上的父类,以及父类的父类,一直到NSObject。
initialize的调用
四、initialize的调用顺序
initialize的调用顺序和普通的方法一样,在二十三章中的attachLists中。
如果分类与类有同名方法,在attachLists的时候,存储方法的array会将先添加到array中的方法移到后面,后添加的同名方法则会放在前面,所以不是类和其他分类的同名方法被替换了,而是因为在调用的时候,objc_msgSend找到前面的SEL对应的IMP就会去go done了,不会找后面的同名方法了。
所以initialize的调用顺序也是直接调用最后加载到methodsList中的分类的initialize方法。
五、总结
initialize的实现和lookUpImpOrForward有关,也就是和类的方法调用有关。所以它的执行顺序很很早。- 类有分类,则类调用
initialize的时候,只会调用到最后一个加载到内存中的分类的initialize。