1. load
1.1 概述
先看下官網(wǎng)文檔給出的說明
The load message is sent to classes and categories that are both dynamically loaded and statically linked, but only if the newly loaded class or category implements a method that can respond.
The order of initialization is as follows:
All initializers in any framework you link to.
All +load methods in your image.
All C++ static initializers and C/C++ attribute(constructor) functions in your image.
All initializers in frameworks that link to you.
In addition:
A class’s +load method is called after all of its superclasses’ +load methods.
A category +load method is called after the class’s own +load method.
In a custom implementation of load you can therefore safely message other unrelated classes from the same image, but any load methods implemented by those classes may not have run yet.
從文檔中我們可以簡單得到:
首先我們必須在類或者分類中實(shí)現(xiàn) load 方法伴挚,這樣才能程序的初始化中被調(diào)用。
我們iOS程序初始化時(shí)异赫,我們編譯的好的類和分類都會(huì)被直接加載到內(nèi)存中返敬,在framework初始化方法調(diào)用完成后遂庄,就會(huì)開始調(diào)用我們代碼中所有類實(shí)現(xiàn)的load的方法。這個(gè)過程和類是否被調(diào)用或者引用沒有關(guān)系劲赠,只要該類參與了編譯涛目,并且實(shí)現(xiàn)了load的方法,那么他就會(huì)被調(diào)用凛澎。所以load方法的調(diào)用是在main 函數(shù)被執(zhí)行之前的霹肝。
1.2 調(diào)用順序
假設(shè)一個(gè)類,它本身塑煎、子類沫换、所有分類都實(shí)現(xiàn)了load方法,那么它的調(diào)用順序時(shí)怎么樣的最铁?
load 方法執(zhí)行規(guī)則:
- 先執(zhí)行所有類的load方法讯赏,再執(zhí)行所有分類的load方法垮兑。
- 執(zhí)行類的load方法時(shí),是按照參與編譯的順序漱挎,先編譯的類先執(zhí)行甥角,但是如果某個(gè)類是繼承自另一個(gè)類,那么會(huì)先執(zhí)行父類的load方法個(gè)再執(zhí)行自己的load方法识樱。
- 執(zhí)行分類的load方法時(shí),是按照分類參與編譯的順序震束,先編譯的分類先執(zhí)行怜庸。
編譯順序,如果使用xcode,我們可以在項(xiàng)目的Build Phases --> Compile Sources查看垢村,最上面的就最先編譯割疾,我們可以拖動(dòng)文件來調(diào)整編譯順序。
1.3 方法調(diào)用原理
load方法和普通方法調(diào)用的方式不一樣嘉栓。普通方法調(diào)用是通過消息發(fā)送機(jī)制實(shí)現(xiàn)的宏榕,會(huì)先去類或元類的方法列表中查找,如果找到了方法就執(zhí)行侵佃,如果沒有找到就去父類的方法列表里面找麻昼,只要找到就會(huì)終止查找,所以只會(huì)執(zhí)行一次馋辈。
load方法調(diào)用時(shí)抚芦,每個(gè)類都是根據(jù)load方法的地址直接調(diào)用,而不會(huì)走objc_msgSend函數(shù)的方法查找流程迈螟,也就是說一個(gè)類有實(shí)現(xiàn)load方法就執(zhí)行叉抡,沒有就不執(zhí)行(沒有的話也不會(huì)去父類里面查找)。
1.4 應(yīng)用
通常會(huì)在load方法里面進(jìn)行方法交換(Method Swizzle)答毫,沒有必要話盡量不要在load方法里面實(shí)現(xiàn)過多的代碼褥民,尤其是一些耗時(shí)性的。另外也不要去直接調(diào)用其他類洗搂,因?yàn)槠渌惪赡苓€沒有加載完全消返。
2 initialize
2.1 概述
The runtime sends initialize() to each class in a program just before the class, or any class that inherits from it, is sent its first message from within the program. Superclasses receive this message before their subclasses.
The runtime sends the initialize() message to classes in a thread-safe manner. That is, initialize() is run by the first thread to send a message to a class, and any other thread that tries to send a message to that class will block until initialize() completes.
The superclass implementation may be called multiple times if subclasses do not implement initialize()—the runtime will call the inherited implementation—or if subclasses explicitly call [super initialize]. If you want to protect yourself from being run multiple times, you can structure your implementation along these lines:
+ (void)initialize {
if (self == [ClassName self]) {
// ... do the initialization ...
}
}
Because initialize() is called in a blocking manner, it’s important to limit method implementations to the minimum amount of work necessary possible. Specifically, any code that takes locks that might be required by other classes in their initialize() methods is liable to lead to deadlocks. Therefore, you should not rely on initialize() for complex initialization, and should instead limit it to straightforward, class local initialization.
Special Considerations
initialize() is invoked only once per class. If you want to perform independent initialization for the class and for categories of the class, you should implement load() methods.
官方文檔上可知:
initialize方法是在類或它的子類收到第一條消息時(shí)被調(diào)用的,并且是線程安全的蚕脏,這里的消息就是指實(shí)例方法或類方法的調(diào)用侦副,所以所有類的initialize調(diào)用是在執(zhí)行main函數(shù)之后調(diào)用的。而且一個(gè)類只會(huì)調(diào)用一次initialize方法驼鞭。如果一個(gè)類在程序運(yùn)行過程中一直沒有被使用過秦驯,那這個(gè)類的initialize方法也就不會(huì)被調(diào)用,這一點(diǎn)和load方法是不一樣的挣棕。
另外父類一定是早于子類收到initialize 的消息的译隘,如果子類沒有實(shí)現(xiàn)initialize 方法亲桥,那么第一次收到消息時(shí),就會(huì)調(diào)用父類的initialize方法固耘。
如果子類和父類都實(shí)現(xiàn)了initialize方法题篷,那么會(huì)先調(diào)用父類的方法,然后調(diào)用子類的方法個(gè)(<u>這里注意子類中不需要寫[super initialize]來調(diào)用父類的方法厅目,通過查看源碼得知它是在底層實(shí)現(xiàn)過程中主動(dòng)調(diào)用的父類的initialize方法</u>)番枚。
需要注意的是如果在分類中實(shí)現(xiàn)了initialize方法,那么他會(huì)覆蓋掉原來類對initialize的實(shí)現(xiàn)损敷。
2.2 調(diào)用規(guī)則例子
假設(shè) Person 為基類實(shí)現(xiàn)了initialize方法葫笼, Student和Player為子類且實(shí)現(xiàn)了initialize方法, Teacher和Driver為子類但是沒有實(shí)現(xiàn) initialize方法拗馒。
Student *s = Student.new;
Person *p = Person.new;
Player *player = Player.new;
Teacher *t = Teacher.new;
Driver *d = Driver.new;
那么打印log順序?yàn)椋?/p>
+[Person initialize]
+[Student initialize]
+[Player initialize]
+[Person initialize]
+[Person initialize]
- 因?yàn)镾tudent 是Person的子類路星,所以Person會(huì)優(yōu)先執(zhí)行,然后才到Student.
- 因?yàn)镻erson已經(jīng)執(zhí)行過了initialize诱桂,所以不再執(zhí)行
- Person已經(jīng)執(zhí)行過洋丐,所以只會(huì)執(zhí)行Player的方法
- Teacher和Driver都沒有實(shí)現(xiàn)initialize,所以都直接調(diào)用了他們父類(Person)的方法
2.3 應(yīng)用
實(shí)際開發(fā)中initialize方法一般用于初始化全局變量或靜態(tài)變量挥等。雖然使用initialize要比使用load安全(因?yàn)樵谡{(diào)用initialize時(shí)所有類已經(jīng)被加載進(jìn)內(nèi)存了)友绝,但我們還是要盡量少用initialize這個(gè)方法個(gè),尤其要謹(jǐn)慎在分類中實(shí)現(xiàn)initialize方法肝劲,因?yàn)槿绻诜诸愔袑?shí)現(xiàn)了九榔,本類實(shí)現(xiàn)的initialize方法將不會(huì)被調(diào)用。