底层iOS基础知识

+load和+initialize总结

2021-07-20  本文已影响0人  鄂北

+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

从上面1-4和1-5两个图可以看到,Animal+Big和Animal+Small是Animal的子类,在compile sources中都是在Animal前面加载的,但是他们的load方法却是先加载的Animal然后再加载的分类。

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时,因为\color{red}{在程序运行过程中+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方法,并输出相对应的类名。

- (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,所以打印的结果中会有父类的打印结果。

- (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

因为每个类只会初始化一次

- (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

- (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已经初始化了,所以就不会再初始化了。

- (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]没起到作用。

\color{red}{这5种情况的原因可以在上面贴出来的源码中看出}

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没有影响

上一篇下一篇

猜你喜欢

热点阅读