二十五、initialize

718 阅读2分钟

本文由快学吧个人写作,以任何形式转载请表明原文出处。

一、资料准备

objc4-818.2

对应mac的版本是11.1。可根据自己的系统版本挑选可以进行调试的源码。

二、思路

  1. 前面一直在说类和分类的加载和实现,加载是load,并且已经看过了,那么实现是什么呢?
  2. initialize的调用时机也比main函数要早,那么initialize是在哪里被执行的?
  3. 类和分类的initialize是否和load一样,有着执行顺序。

三、探索initialize的方法

1. 代码准备和思路准备 :

在main.m的main函数中初始化JDMan。

图片.png

图片.png

然后在JDMan.m中实现+ (void)initialize方法。运行项目,看控制台打印 :

图片.png

在load方法后,在main函数执行前,initialize方法会进行调用。

然后更改main.m中的JDMan的调用 :

图片.png

看控制台打印 :

图片.png

依然是在load方法之后,在main函数执行之前,initialize方法会进行调用。

并且不会因为JDMan从调用alloc变成调用class,就不调用initialize。也就是说,initialize和类调用哪个方法没关系,主要是和类被使用了有关系,或者更精确的说,是和类发送消息,调用方法有关。

所以initialize的实现,应该是在方法调用中,也就是objc_msgSend中,或者在lookUpImpOrForward中。而objc_msgSend是汇编,在汇编中是没有见到initialize的调用的,详情见之前的文章。那么lookUpImpOrForward中是否有之前忽略的点,实现了initialize呢?

2. initialize的实现

进入lookUpImpOrForward中找到有关类的初始化 :

图片.png

进入源码 :

图片.png

图片.png

图片.png

initializeNonMetaClass源码很多,比较重要的就两点 :

  1. 初始化类,会先递归循环初始化继承链上的父类,以及父类的父类,一直到NSObject。

图片.png

  1. initialize的调用

图片.png

图片.png

四、initialize的调用顺序

initialize的调用顺序和普通的方法一样,在二十三章中的attachLists中。

如果分类与类有同名方法,在attachLists的时候,存储方法的array会将先添加到array中的方法移到后面,后添加的同名方法则会放在前面,所以不是类和其他分类的同名方法被替换了,而是因为在调用的时候,objc_msgSend找到前面的SEL对应的IMP就会去go done了,不会找后面的同名方法了。

所以initialize的调用顺序也是直接调用最后加载到methodsList中的分类的initialize方法。

五、总结

  1. initialize的实现和lookUpImpOrForward有关,也就是和类的方法调用有关。所以它的执行顺序很很早。
  2. 类有分类,则类调用initialize的时候,只会调用到最后一个加载到内存中的分类的initialize