今天我们就围绕一个面试题来从源码的角度分析答案!
一、Category中有load方法吗?load方法是什么时候调用的?load方法能继承吗?如果分类又存在继承是如何加载load顺序的呢?
首先我们先看下下面的代码,我们先看代码运行结果,再从源码上分析!

GDPerson.m文件里面+(void)load{ NSLog(@"GDPerson+load"); } GDPerson+Test1.m文件里面+(void)load{ NSLog(@"GDPerson+Test1+load");}GDPerson+Test2.m文件里面+(void)load{ NSLog(@"GDPerson+Test2+load");}
1.GDPerson+Test1和GDPerson+Test2是GDPerson分类;
2.GDStudent+Test1和GDStudent+Test2是GDStudent分类;
3.GDStudent继承于GDPerson
从上面的结果我们可以看出,分类是在加载分类的时候调用load方法,没有任何操作,是主动调用。
二、GDPerson和GDPerson+Test1和GDStudent+Test2调用顺序和编译顺序有关吗?
请看下面的截图:

我们无论怎么调整编译顺序,GDPerson的load方法总是先输出,剩下2个分类是按照分类的顺序加载load方法。
然后我们在GDPerson和它的分类里面都加一个+(void)test方法,按照上面的顺序,我们知道肯定优先执行GDStudent+Test2的test,我想具体原因就不说了,我之前的博客都有好好介绍这些问题
思考
从之前我们知识体,我们知道类方法都是存在元类对象中的,我们也知道+(void)test肯定也是存在元类对象中的,那么GDPerson类和分类的三个+(void)test肯定是在元类对象,那么+(load)是什么样的呢?这里我们可以看一下元类对象里面到底存了哪些方法,还记得我之前的一篇博客吗?有一个方法可以获取对象中的方法(探索KVO的本质(二)),可以这里面查看。
我再贴一遍方法:


从这里可以看出,load方法是合并在元类对象里面的,这也跟我们之前的博客说的知道点是吻合的
窥探源码来论证我们上面说的结论
源码地址,我之前几个博客都有说,这里就不说了,直接打开objc4最新源码查看:
我先把查看源码顺序贴上,再和大家一起看一下。

objc-os.mm就是程序的入口,_objc_init初始化;load_image,image是代表镜像,load_image正好是我们要的load的方法,接下来我就一步一步的带着看一下


这里很清楚的解决了我们的疑问,类的load方法先调用,分类的load方法后调用。继续看call_class_loads具体是怎么调用的

这里我们清楚了,这个不像我们之前说的,是通过直接找的内存地址直接调用,而不是我们之前的先通过isa找到类对象,再通过类对象的isa找到元类...等等,所以是运行时就会直接调用load方法。
接下来分类的方法也是同样的道理,大家可以自己看一下,这里就不说了。相信大家心中都有答案。
接下来我们看一个更复杂的情况
如果出现继承会是怎么样的呢?
下面我把GDStudent是继承GDPerson,然后GDStudent+test1和GDStudent+test2是GDStudent分类

无论怎么修改编译顺序,父类的load方法都是优先调用,再调用本类,再根据编译顺序,调用其他分类,这个大家可以自己去验证验证,接下来我们用源码分析看看到底是不是这样。
出现继承的源码分析调用顺序
先看类再看分类,我们还是先看call_class_loads方法的实现

所以接下来我们就去研究classes的顺序,在调用之前有个prepare_load_methods方法,那很有可能就是在这里面准备了classes的顺序,我们进去看一下有个schedule_class_load,schedule是规划的意思,就是规划我们的类加载,那就接着看(过程比较多,也比较简单,我标记的比较清楚,很容易尝试),请看下面的图

从上面的图可以看出数组的顺序是父类在前面,而且是schedule_class_load是递归调用会调用所有父类,所以是优先加载。也是优先调用,把我们在看一下add_class_to_loadable_list(cls)的具体实现吧

说了这么多,我们终于可以总结了😄
总结:
+load方法会在runtime加载类、分类时调用
每个类、分类只会在程序运行过程中调用一次
调用顺序:
1.首先会调用类的load方法;
2.调用子类的load方法之前会优先调用父类的load方法;
3.按照编译的顺序(先编译,先调用)
4.再调用分类的load方法,按照先编译,先调用
接下来我们解答面试题
load方法的继承比较简单,我就不说了,它也是消息机制发送,通过isa,所以它会优先调用分类的load,这个大家自己尝试!
一、Category中有load方法吗?load方法是什么时候调用的?load方法能继承吗?
有load方法(这个问法就很奇怪)
load方法是在runtime加载类、分类的时候调用
load方法可以继承,但是一般情况下不会主动调用load方法,都是让系统自动调用
网友评论