前言
根据上篇文章---- 程序加载流程的探究,知道了dyld的宏观加载流程,还有dyld和_objc_init对接调用关系。通过_dyld_objc_notify_register函数,知道主要是传值&map_images和load_images:
对
map_images和load_images函数的调用,也有简单的介绍,那么这篇文章,则对上篇进行一些延伸。
资源准备
objc源码下载地址:多个版本的objc
进入正文
load_images()
在
load_images()函数里面做了:
- 判断是否加载分类,如果没有,就加载所有分类
loadAllCategories(); - 准备加载方法
prepare_load_methods(); - 调用加载方法
call_load_methods();
prepare_load_methods()
那么我们看下是如何准备的了?
准备流程:
- 通过
for循环,递归地为cls和任何未加载的父类安排加载---schedule_class_load()。 - 通过
for循环,与外部的真实关联、cls类初始化、分配读写数据、返回类的真实类结构 ---realizeClassWithoutSwift()。 - 通过
for循环,将类别添加到可加载列表 ---add_category_to_loadable_list();
schedule_class_load()
通过这个源码块,可以知道:
- 通过递归,先把父类的
load写入表里面,然后再写入子类的load---schedule_class_load(); - 将类添加到可加载列表 ---
add_class_to_loadable_list();
add_class_to_loadable_list()
通过这个源码块,可以知道:
- 如果父类和子类还有没进行
load加载的,直接返回,继续递归加载; - 如果没有加载方法,实现一个加载方法,就重新加载;
- 将类添加到可加载列表 --
loadable_classes;
add_category_to_loadable_list()
分类的处理和类的处理大致相同。最后将分类添加进入
loadable_categories里面
当准备加载完毕后,就是调用加载了。
call_load_methods()
通过
do-while循环,先调用类的load,然后再调用分类的load。
call_class_loads()
- 类的
load调用:通过loadable_classes里面拿到load方法,并调用。
call_category_loads()
- 分类的
load调用:通过loadable_categories里面拿到load方法,并调用。
load函数小结
通过上文的分析,当执行load_images时,先是准备,先把类的load写入loadable_classes里面,再把分类的load写入loadable_categories里面;再是调用,先是类的load调用,从loadable_classes里面获取,再是分类的load调用,从loadable_categories里面获取。
可以再通过一个案例来验证下:创建LGPerson继承于NSObject,LGSon继承于LGPerson,LGGrandSon继承于LGSon,再在他们三者各自的.m文件,调用
+ (void)load{
NSLog(@"%s", __func__);
}
最后在main()里面初始化LGGrandSon这个类:
这样就印证了我们的分析。
一提到load函数,立马就想到了initialize(初始化)函数。这也是在面试中,经常会问到的。那么接下来,就对initialize进行分析,和load函数做个对比。
initialize()
根据消息查找流程,我们知道在消息的慢速查找里面lookUpImpOrForward(),有类的实现realizeAndInitializeIfNeeded_locked(),入下图所示:
realizeAndInitializeIfNeeded_locked()
从realizeAndInitializeIfNeeded_locked()函数里面分析,类必须是先要实现,也就是先的进行load(),然后才能initialize()初始化,如下图:
该函数的工作内容:
- 判断类是否
实现,如果没实现,就先进行实现; - 当类实现后,再判断类是否
初始化,如果没有初始化,就进行初始化。
initializeAndMaybeRelock()
该函数的工作内容:
- 检测类是否已经初始化,如果初始化,就直接返回;
- 检测非元类是否实现,如果没有实现,就需要进行实现;
- 初始化非元类。
initializeNonMetaClass()
该函数的工作内容:
递归初始化,从父类开始----initializeNonMetaClass();- 当是进行初始化是,直接调用
callInitialize初始化函数;
callInitialize()
该函数的工作内容:
initialize是通过obj_msgSend发送消息,所以其调用方式是:消息发送;
继续沿用刚才的例子,在LGPerson、LGSon、LGGrandSon三者各自的.m文件,添加:
+ (void)initialize
{
NSLog(@"%s", __func__);
}
接着再运行工程:
根据打印结果,可以知道:
- 先调用父类的
initialize,后调用子类的initialize; - 当分类中也有
initialize,就会重写类里面的initialize; load先调用,initialize后调用。
initialize和load对比
initialize和load有本质的却别:
load函数是在应用程序加载阶段,dyld流程中自动调动的;initialize是在向类发送消息的时候才会被触发。