load
-
+load
方法会在runtime
加载类、分类是调用,每个类、分类的 load 方法,在程序运行过程中只调用一次。 - 调用顺序:
1.先调用类的load
方法,按照编译的先后顺序调用(先编译先调用),调用子类的load
之前会先调用父类的load
方法
2.再调用分类的load
方法,哪找编译的先后顺序调用(先编译先调用) -
load
方法可以继承,但一般不手动调用,而是让系统调用,系统会在加载类的时候调用,这个时候是直接找到类的load
方法调用;如果手动调用的话,会走消息机制调用。
load 函数调用解析
在运行时加载程序时会调用call_load_methods()
函数
void call_load_methods(void) {
....
do{
while(loadable_classes_used > 0) {
call_class_loads(); //调用类的+load 方法
}
more_categories = call_category_loads(); //调用分类的 +load 方法
} while (loadable_classes_used > 0 || more_categories);
...
}
根据以上代码我们可以知道,先调用类的 load
方法,类的 load
方法调用完才会调用分类的load
方法。那么这里为什么跟我们OC 的消息机制不一样呢?oc 的消息机制不是去方法列表里面去找,找到后直接调用吗?如果按照 OC 的消息机制先找到的应该是分类中的方法,应该调用分类的方法才正确呀!其实这里主要是因为load
方法并不是走 OC 的消息机制,而是通过类找到load
方法直接调用的。分类的load
方法也是直接找通过分类找到load
方法然后直接调用的
// 类的 load 方法调用
for (i = 0; i < used; i++) {
Class cls =classes[i].cls;
load_method_ t load_method = (load_method_t)classes[i].method; //取出类的 load 方法直接调用。这里 method 就是指向 load 方法。
....
}
// 分类的 load 方法调用
for (i = 0; i < used; i++){
Category cat = cats[i].cat;
load_method_ t load_method = (load_method_t)cats[i].method;//取出分类的 load 方法直接调用
....
}
//先调用父类的 load 方法,后调用子类的 load 方法.这里是一个递归调用,保证父类先调用,然后调用子类。
static void schedule_class_load(Class cls) {
if(!cls) return;
assert(cls->isRealized());
if(cls->data()->flags & RW_LOADED) return;
schedule_calss_load(cls->superclass);// 保证先调用父类的
add_class_to_loadable_list(cls); //将 cls 天剑到 loadable_classes数组的最后面
cls->setInfo(RW_LOADED);
}
initialize
-
initialize
调用是通过消息机制调用的。所以分类有 initialize的话会调用分类的initialize
-
initialize
会在类第一次收到消息的时候调用。 - 调用顺序
1.先调用父类的+initialize
,再调用子类的+initialize
,先初始化父类,再初始化子类,每个类只会初始化一次。 -
initialize
是通过objc_msgSend
调用的,如果子类没有实现initialize
方法就会调用父类的initialize
方法,所以父类的initialize
可能会被调用多次。如果分类实现了initialize
就会覆盖类本身的initialize
调用
initialize 方法调用解析
- 当我们调用一个方法时,会先去查找这个方法,在查找这个方法之前我们先去看这个类有没有初始化过,如果没有初始化过,我们就先调用
initialize
去初始化,在调用initialize
初始化之前会先判断父类有没有初始化过,如果父类没有初始化过就先初始化父,然后再调用自己的initialize
,完成初始化过程,如果当前类已经初始化过,就不会再调用initialize
去初始化了
load 和 initialize 的区别
- 调用方式:
load
是根据函数地址直接调用,initialize 是通过消息机制(objc_msgSend
)调用 - 调用时间:
load
是runtime
加载类分类的时候调用的(只会调用一次),initialize
是类第一次收到消息的时候调用,每一个类只会 initialize 一次(父类的initialize
方法可能被调用多次) - 调用顺序 :
load
先调用类的load
,后调用分类的load
,调用子类的load
之前会先调用父类的load
;类的initialize 会被分类的initialize
覆盖,先初始化父类,再初始化子类(如果子类没有initialize
,最终调用的是父类的initialize
方法)
网友评论