先來看一個表
方法 | +(void)load | +(void)initialize |
---|---|---|
執(zhí)行時機 | 在程序運行后立即執(zhí)行 | 在類的方法第一次被調時執(zhí)行 |
若自身未定義客叉,是否沿用父類的方法? | 否 | 是 |
類別中的定義 | 全都執(zhí)行褂痰,但后于類中的方法 覆蓋類中的方法, | 只執(zhí)行一個 |
執(zhí)行次數(非主動調用的情況下) | 必然一次 | 0症虑、1缩歪、多 次(調用者會不同) |
先看官方解釋:
一、+load
load 方法在什么時候調用谍憔?
官方解釋是:運行時匪蝙,添加類或者分類的時候調用主籍。實現此方法以在加載時執(zhí)行特定于類的行為。
+load
方法是一定會在runtime中被調用的逛球,只要類被添加到runtime中了千元,就會調用+load
方法,即只要是在Compile Sources
中出現的文件總是會被裝載颤绕,與這個類是否被用到無關幸海,因此+load
方法總是在main函數之前調用。所以我們可以自己實現+laod
方法來在這個時候執(zhí)行一些行為奥务。
換句話說物独,load是在app啟動的時候加載,從源碼中找
_objc_init —>
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
這里面的2個方法 map_images 和 load_images, map_images的作用就是加載所有的類/協議/分類氯葬,加載完成之后挡篓,就開始調用load_images,在這個方法里面看:
load_images(const char *path __unused, const struct mach_header *mh)
{
......
{
mutex_locker_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh); // 把所有需要load的類 加載一個list里面
}
call_load_methods(); // 調用load方法
}
void call_load_methods(void)
{
......
do {
while (loadable_classes_used > 0) {
call_class_loads(); // 先加載類的load
}
more_categories = call_category_loads(); // 在加載category的load
} while (loadable_classes_used > 0 || more_categories);
......
}
另外有意思的一點是帚称,+load
方法不會覆蓋官研。也就是說,如果子類實現了+load
方法闯睹,那么會先調用父類的+load
方法戏羽,然后又去執(zhí)行子類的+load
方法。但要注意的時+load方法只會調用一次瞻坝。而且執(zhí)行順序是:類 ->父類-> 子類 ->分類蛛壳。而不同類之間的順序不一定。
但是這里依然有一個疑問所刀,官方解釋沒有說清楚,1捞挥、分類的加載順序是怎樣的浮创?
其實在源碼中有可以看到:
void prepare_load_methods(const headerType *mhdr)
{
......
classref_t *classlist =
_getObjc2NonlazyClassList(mhdr, &count); // 1、按照編譯順序加載所有的類(不包括分類)
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i])); // 2砌函、在這里 按照先父類 在子類的方式加入列表
}
category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count); // 分類也是類似的方式
for (i = 0; i < count; i++) {
......
add_category_to_loadable_list(cat);
}
}
static void schedule_class_load(Class cls)
{
.......
schedule_class_load(cls->superclass); //遞歸加載斩披,先加載父類
add_class_to_loadable_list(cls);
}
由以上代碼中 1、2備注讹俊,得出 :
1垦沉、先加載類的load:類的加載是按照編譯順序,同時遵循先父類再子類的方式
2仍劈、再加載分類的load:分類直接按照編譯順序厕倍,和其綁定類的繼承沒有關系
總結
1、先加載類的load
2贩疙、在加載分類的load
3讹弯、不同的類之間加載順序為:有繼承關系的况既,先加載父類load、再加載子類的load组民,無繼承關系的棒仍,按照編譯順序
----比如順序二 Student、OtherClass臭胜、Person莫其,先加載Student的load,由于Person是Student的父類耸三,所以Person的順序比OtherClass早
4乱陡、分類的加載順序是完全按照編譯順序,也就是誰在前面吕晌,誰先加載蛋褥。和其綁定類的繼承關系無關
----比如順序二中,Student繼承Person睛驳,但是其分類的順序是 Student+JE2烙心、Student+JE1、Person+JE乏沸,順序是什么樣淫茵,加載load就是什么樣。
5蹬跃、即使有類的源文件匙瘪,但是編譯列表中沒有,那么這個類就不會被編譯蝶缀,也就不會執(zhí)行其load方法
二丹喻、+initialize
initialize方法在什么時候調用?
官方解釋是:在類收到第一條消息之前初始化它翁都。
換句話說碍论,就是第一次用到它之前調用,比如初始化一個對象(其父類也會調用initialize)柄慰、調用類方法等鳍悠。 從源碼中找:
說明:沒有發(fā)送消息的類不會調用initialize
如果主類有相應的分類(或多個分類),會調用分類中的initialize方法坐搔,具體調用的是哪個分類的方法藏研,由編譯順序決定。
當子類沒有重寫initialize方法概行,這個時候回去執(zhí)行父類的initialize方法
class_initialize -> initializeNonMetaClass()
void initializeNonMetaClass(Class cls)
{
.......
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
initializeNonMetaClass(supercls); // 遞歸加載父類的initialize
}
........
}
+initialize
方法是在類或它的子類收到第一條消息之前被調用的蠢挡,這里所指的消息包括實例方法和類方法的調用,并且只會調用一次。+initialize
方法實際上是一種惰性調用袒哥,也就是說如果一個類一直沒被用到缩筛,那它的+initialize
方法也不會被調用,這一點有利于節(jié)約資源堡称。
與+load
方法不同瞎抛,卻更符合我們預期的就是,+initialize
方法會覆蓋却紧。也就是說如果子類實現了+initialize
方法桐臊,就不會執(zhí)行父類的了,直接執(zhí)行子類本身的晓殊。如果分類實現了+initialize
方法断凶,也不會再執(zhí)行主類的。所以+initialize
方法的執(zhí)行覆蓋順序是:分類 -> 子類 ->類巫俺。且只會有一個+initialize
方法被執(zhí)行认烁。
總結
1、initialize的執(zhí)行順序為先父類介汹、在子類
2却嗡、分類的initialize方法會覆蓋主類的方法(假覆蓋,方法都在嘹承,只是沒有執(zhí)行)
3窗价、只有在這個類有發(fā)送消息的時候才會執(zhí)行initialize,比如初始化對象叹卷、調用類方法等撼港。
4、多個分類的情況骤竹,只執(zhí)行一次帝牡,具體執(zhí)行哪個分類的initialize,有編譯順序決定(Build Phases -> Compile Sources 中的順序)
5蒙揣、如果子類沒有重寫initialize否灾,那么會調用其父類的initialize方法
三、兩者的異同
1鸣奔、相同點
1).load和initialize會被自動調用,不能手動調用它們惩阶。
2).子類實現了load和initialize的話挎狸,會隱式調用父類的load和initialize方法。
3).load和initialize方法內部使用了鎖断楷,因此它們是線程安全的锨匆。
2、不同點
1).調用順序不同,以main函數為分界恐锣,+load
方法在main函數之前執(zhí)行茅主,+initialize
在main函數之后執(zhí)行。
2).子類中沒有實現+load
方法的話土榴,不會調用父類的+load
方法诀姚;而子類如果沒有實現+initialize
方法的話,也會自動調用父類的+initialize
方法玷禽。
3).+load
方法是在類被裝在進來的時候就會調用赫段,+initialize
在第一次給某個類發(fā)送消息時調用(比如實例化一個對象)桩撮,并且只會調用一次梅誓,是懶加載模式伦意,如果這個類一直沒有使用搂捧,就不回調用到+initialize
方法舀射。
四淳地、使用場景
1.+load
一般是用來交換方法Method Swizzle
香嗓,由于它是線程安全的驶拱,而且一定會調用且只會調用一次额获,通常在使用UrlRouter的時候注冊類的時候也在+load
方法中注冊够庙。
2.+initialize
方法主要用來對一些不方便在編譯期初始化的對象進行賦值,或者說對一些靜態(tài)常量進行初始化操作咪啡。