+load和+initialize总结
+load总结
1、+load在runtime加载类和分类时调用,且在程序运行过程中+load方法只会调用一次。
2、调用顺序,先调用父类load,再调用子类load,最后调用分类load
1-1.png在源码中可以看到是先调用类的call_class_loads,等call_class_loads方法遍历调用完后再调用分类call_category_loads。
在call_class_loads源码中有个下面的注释 1-2.png
从注释中可以看到在加载类的load方法时会先加载父类的load,所以总结下来就是先调用父类load,再调用子类load,最后调用分类load
3、当调用子类的load时会先调用父类的load
4 、类和分类的调用顺序是根据类的编译顺序 1-3.png
-
因为load的调用顺序是遵循1、2、3的,所以在compile sources中不管分类是否在类的前后,在调用load时都会在类的后面加载。但是分类和分类的load加载顺序会遵循在compile sources中的加载顺序。
1-4.png
1-5.png
从上面1-4和1-5两个图可以看到,Animal+Big和Animal+Small是Animal的子类,在compile sources中都是在Animal前面加载的,但是他们的load方法却是先加载的Animal然后再加载的分类。
- 类和类也会遵循在compile sources中的加载顺序,但是有个需要注意的是,。如果父类在子类的后面,那么在加载子类的load时候会先调用父类的load再调用子类的load,但编译到父类时因为前面加载子类的load时候已经加载过了父类的load,所以此时就不会再加载一次父类的load(因为+load在runtime加载类和分类时调用,)。
1-6.png
1-7.png
从1-6和1-7中可以看出,因为Dog为Animal的子类,所以在加载Dog的load时会先加载父类的Animal的load,所以打印的结果为
2021-07-20 10:05:46.035020+0800 loadTest[3950:70858] Animal load
2021-07-20 10:05:46.035607+0800 loadTest[3950:70858] Dog load
当加载People的load时,因为People本来就是父类所以接下来打印的结果是
2021-07-20 10:05:46.035020+0800 loadTest[3950:70858] Animal load
2021-07-20 10:05:46.035607+0800 loadTest[3950:70858] Dog load
2021-07-20 10:05:46.035742+0800 loadTest[3950:70858] People load
当加载到Animal的load时,因为,Animal的load在调用子类Dog时已经调用了,所以此时不会调用。
于是会跳过Animal去加载下个类Teacher的load。此时打印结果为
2021-07-20 10:05:46.035020+0800 loadTest[3950:70858] Animal load
2021-07-20 10:05:46.035607+0800 loadTest[3950:70858] Dog load
2021-07-20 10:05:46.035742+0800 loadTest[3950:70858] People load
2021-07-20 10:05:46.035867+0800 loadTest[3950:70858] Teacher load
5、+load方法是根据方法地址直接调用,并不是经过objc_msgSend函数调用。
6、尽量不在load的方法里进行耗时操作,因为load是在类编译的时候调用的,在load中进行耗时操作会增加的app启动时间。
+initialize总结
1、+initialize方法会在类第一次接受到消息时调用。
2、先调用父类的+initialize,再调用子类的+initialize
1-1.png从源码中可以看到是先调用父类的再调用子类,在调用前先判断了是否初始化了,每个类只会初始化一次。
我创建了Dog、Cat两个继承至Animal的类,在三个类里分别调用了initialize方法,并输出相对应的类名。
- 1、单独调用Dog的方法
- (void)viewDidLoad {
[super viewDidLoad];
[Dog alloc];
}
打印结果
2021-07-20 14:05:36.412311+0800 loadTest[6668:215762] Animal initialize
2021-07-20 14:05:36.412485+0800 loadTest[6668:215762] Dog initialize
因为在调用子类方法的+initialize会调用父类的+initialize,所以打印的结果中会有父类的打印结果。
- 2、调用两次Dog方法
- (void)viewDidLoad {
[super viewDidLoad];
[Dog alloc];
[Dog alloc];
}
打印的结果和1一样
2021-07-20 14:13:10.570016+0800 loadTest[6767:221564] Animal initialize
2021-07-20 14:13:10.570175+0800 loadTest[6767:221564] Dog initialize
因为每个类只会初始化一次
- 3、调用Dog和Animal
- (void)viewDidLoad {
[super viewDidLoad];
[Dog alloc];
[Animal alloc];
}
打印结果和1、2一样
2021-07-20 14:15:49.393094+0800 loadTest[6824:224205] Animal initialize
2021-07-20 14:15:49.393243+0800 loadTest[6824:224205] Dog initialize
这打印的结果是由[Dog alloc]引起的,而[Animal alloc]并没有调用+initialize,因为在Dog调用+initialize时已经使Animal初始化了,而每个类只会初始化一次,所以[Animal alloc]并没有调用+initialize
- 4、当先调用Animal再调用Dog
- (void)viewDidLoad {
[super viewDidLoad];
[Animal alloc];
[Dog alloc];
}
打印结果
2021-07-20 14:21:16.077371+0800 loadTest[6889:228339] Animal initialize
2021-07-20 14:21:16.077572+0800 loadTest[6889:228339] Dog initialize
这次打印的结果虽然和3是一样的,但是Animal initialize并不是[Dog alloc]引起的,而是[Animal alloc]引起的。因为每个类只会初始一次,所以在[Dog alloc]时发现Animal已经初始化了,所以就不会再初始化了。
- 5、依次调用Dog、Cat、Animal
- (void)viewDidLoad {
[super viewDidLoad];
[Dog alloc];
[Cat alloc];
[Animal alloc];
}
打印结果
2021-07-20 14:26:04.491833+0800 loadTest[6959:232652] Animal initialize
2021-07-20 14:26:04.492040+0800 loadTest[6959:232652] Dog initialize
2021-07-20 14:26:04.492174+0800 loadTest[6959:232652] Cat initialize
前两条打印是由[Dog alloc],Cat的+initialize是由[Cat alloc]引起,[Animal alloc]没起到作用。
3、+initialize通过objc_msgSend消息机制进行调用
4、当子类没有实现+initialize时,会调用父类的+initialize,因此父类的+initialize可能会被调用多次
因为+initialize是通过objc_msgSend消息机制调用的,所以当子类没有实现+initialize时,会通过isa找到父类的+initialize
创建一个Pig类继承至Animal,pig类不实现+initialize方法,调用[Pig alloc]
- (void)viewDidLoad {
[super viewDidLoad];
[Pig alloc];
}
打印结果
2021-07-20 14:32:56.878694+0800 loadTest[7056:238119] Animal initialize
2021-07-20 14:32:56.878880+0800 loadTest[7056:238119] Animal initialize
会发现Animal的initialize被调用了两次,第一次是父类调用的,第二次是子类在调用自己的initialize时,发现没实现,然后通过isa找到父类的+initialize
+load和+initialize区别
1、调用时机不同
+load是在类编译时调用,+initialize是在类第一次发送消息时调用
2、方法调用方式不同
+load是根据方法地址直接调用,+initialize是通过objc_msgSend消息机制调用
3、调用次数不同
+load方法只会调用一次,而父类的+initialize有可能会调用多次
4、调用顺序不同
+load的调用顺序跟类的编译顺序有关,+initialize是跟类的第一次发送有关
5、分类的影响
如果分类实现了+initialize,会覆盖类本身的+initialize调用,而对+load没有影响