美文网首页底层iOS基础知识
+load和+initialize总结

+load和+initialize总结

作者: 鄂北 | 来源:发表于2021-07-20 10:37 被阅读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

    • 因为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中的加载顺序,但是有个需要注意的是,\color{red}{父类load永远在子类的load调用之前调用,原因在3中有写到}。如果父类在子类的后面,那么在加载子类的load时候会先调用父类的load再调用子类的load,但编译到父类时因为前面加载子类的load时候已经加载过了父类的load,所以此时就不会再加载一次父类的load(因为+load在runtime加载类和分类时调用,\color{red}{且在程序运行过程中+load方法只会调用一次})。
      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时,因为\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方法,并输出相对应的类名。

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

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

    相关文章

      网友评论

        本文标题:+load和+initialize总结

        本文链接:https://www.haomeiwen.com/subject/tqshmltx.html