+load 方法?
1. 如果一個類實現(xiàn)了load 方法,那么在類被加載到內(nèi)存的時候就會調(diào)用,與這個類是否被用到無關(guān)。執(zhí)行在main函數(shù)之前,此時運行環(huán)境不安全,不能在這份方法里做過多的操作
當(dāng) class 或者 category 添加到 runtime 時調(diào)用。即 load?是在這個文件被程序加載時調(diào)用,注意:在 load方法中向其它類發(fā)送消息,接收消息的類中的+load方法可能尚未被調(diào)用。此時不能保證所有的類被加載完成。
2. 程序中所有的類的 load方法都會被系統(tǒng)自動調(diào)用,包括各種分類,每個類都只調(diào)用一次,并會隱式調(diào)用父類的load 方法。
注意:分類和類 load 方法都會調(diào)用,他們分別存儲在兩個全局表中。是分類 load 不覆蓋原來類的 load 方法的本質(zhì);
兩個 while 循環(huán),先通過?call_class_loads?執(zhí)行所有類的 load 方法,再通過?call_category_loads?執(zhí)行分類的 load 方法。
3. ?調(diào)用類的?load?方法前,先調(diào)用父類的?load方法(遞歸),父類方法優(yōu)先于子類調(diào)用,分類load 方法 最后調(diào)用。
注??: load 直接用函數(shù)地址調(diào)用,不是objc_msgSend 發(fā)送消息,不存在方法查找和消息傳遞機(jī)制;也就是說如果類實現(xiàn)了?load?函數(shù)就會調(diào)用,當(dāng)類未實現(xiàn)load方法時,不會調(diào)用父類load方法。
4. 先編譯的分類, 優(yōu)先調(diào)用load,當(dāng)有多個類別(Category) 都實現(xiàn)了load ?方法,這幾個load方法都會執(zhí)行, 執(zhí)行順序和分類 在 Compile Sources中出現(xiàn)的順序一致。
注意:父類Catregory 與子類 Category 的 load 方法執(zhí)行順序:由分類在編譯器中的編譯順序決定,與其在Compile Sources 出現(xiàn)順序一致。
5. 先編譯的類, 優(yōu)先調(diào)用load,,有多個不同的類的時候, 每個類 load 執(zhí)行順序與 在Compile Sources 出現(xiàn)的順序一致。?
6.?load方法方法內(nèi)部使用了鎖,是線程安全的。有一定的性能開銷會很微弱的影響啟動時間,重寫方法時要盡可能保持簡單,避免阻塞線程,不要再使用鎖。
7.?在 load方法中向其它類發(fā)送消息,接收消息的類中的+load方法可能尚未被調(diào)用。
注意:load調(diào)用時機(jī)比較早,當(dāng)load調(diào)用時,其他類可能還沒加載完成,運行環(huán)境不安全.
8. 調(diào)用優(yōu)先級:父類>子類>分類,并且不會被覆蓋,均會調(diào)用
日常使用場景 :是線程安全的,一定會調(diào)用且只調(diào)用一次
1. 通常在使用UrlRouter的時候注冊類的時候也在+load方法中注冊
2.?用來交換方法Method Swizzle
應(yīng)用:因為類或者分類一旦被加載到運行時,就會調(diào)用這個方法;因為加載時間特別早:所以可以利用這個特性進(jìn)行一些處理
問題:在子類的load?中?[super load] ?會調(diào)用到哪個類中?
?[super load]?將調(diào)用到?類最后一個被編譯的分類的?load?方法,因為這里是消息發(fā)送,而不是通過方法指針。
添加處理category到類的工作會先于類的加載處理 (+load方法的執(zhí)行)。
在load方法中可以調(diào)用category中聲明的方法。
initialize 方法
1. 是懶加載,在類或者其子類第一次使用時調(diào)用。調(diào)用順序在?main 方法之后,實例化對象之前。
注意:即使類文件被引用進(jìn)項目,如果一直沒有使用這個類,方法不會被執(zhí)行。
2. 理論上類的?initialize 方法?只會調(diào)用一次
注意:?initialize 方法是通過 objc_msgSend 調(diào)用的,經(jīng)過方法查找和消息轉(zhuǎn)發(fā)的過程。如果子類沒有實現(xiàn) initialize,就會調(diào)用父類的 initialize 方法,因此父類??initialize 方法?存在多次調(diào)用的可能。
例子:子類不實現(xiàn)initialize方法,會調(diào)用父類的。父類初始化時, 已經(jīng)調(diào)用過一次自己initialize方法. ?父類initialize 方法可能執(zhí)行2次?
使用場景:在initialize 方法中使用hook,做方法替換就可能會出現(xiàn)多次替換,導(dǎo)致方法替換失效。可以使用類型判斷避免這種問題。
3. ?父類調(diào)用一定在子類之前。初始化子類時,系統(tǒng)會默認(rèn)初始化父類先。
??????:如果先引用父類的實例對象,再引用子類實例對象,則會在引用父類實例對象時調(diào)用父類 initialize 方法;用子類實例對象時,由于父類的 initialize 方法已經(jīng)執(zhí)行,所以此時只調(diào)用子類 initialize 方法。
????:初始化自己之前,遞歸執(zhí)行父類的初始化操作;先判斷父類有沒有初始化initialize?完成,如果沒有則遞歸執(zhí)行父類的初始化,父類的initialize 調(diào)用 > 子類initialize 調(diào)用。
4.?分類的 initialize 方法會覆蓋原類的 initialize。(Person、或 Person+Category)都是一個Perosn類,只會執(zhí)行分類的initialize, 原類的initialize 不再執(zhí)行。先初始化分類, 后初始化子類。
注意: 如有多個分類,只執(zhí)行最后被編譯的分類的 initialize 方法,其他分類的?initialize 方法都會失效;生效的是在?Compile Sources列表中最后一個 Category。
5. initialize 方法內(nèi)部使用了鎖,是線程安全的,可能會阻塞線程,方法中應(yīng)做一些簡單不復(fù)雜的類初始化的前期準(zhǔn)備工作。
注意:initialize方法中運行環(huán)境基本健全。,initialize方法一般用于初始化全局變量或靜態(tài)變量。
6. 調(diào)用優(yōu)先級:分類>父類,父類>子類。
如果分類和父類均實現(xiàn)了+initialize,則只有分類的+initialize會被調(diào)用;
如果父類和子類均實現(xiàn)了+initialize,第一次引用 子類時,先調(diào)用父類的+initialize,再調(diào)用子類的+initialize;
如果子類未實現(xiàn)initialize 方法,父類實現(xiàn)了+initialize,則第一次引用子類時,會調(diào)用兩次父類的+initialize
其他:
realizeClass:realizeClass 處理后的類才是『真正的』類,調(diào)用時不能對類做寫操作。
類初始化之前,objc_class->data()?返回的指針指向 class_ro_t?結(jié)構(gòu)體。等 static Class realizeClass(Class cls)?靜態(tài)方法在類第一次初始化時被調(diào)用,它會開辟?class_rw_t?的空間,并將?class_ro_t?指針賦值給?class_rw_t->ro。
懶加載類優(yōu)點:1. 加快啟動速度 ? 2. 節(jié)省應(yīng)用初始內(nèi)存
1. 如果一個類實現(xiàn)了?+ load?方法,這個類是?non-lazy class(非懶加載類)。如果所有的類都在啟動的時候調(diào)用load方法,就會非常慢。
2. 沒實現(xiàn)?+ load?方法,是懶加載類。等到調(diào)用的時候再去實現(xiàn),可以加快啟動速度。
每個類都有很多的代碼,包括變量,方法等,會占用很多內(nèi)存,如果這個類在工程中就沒調(diào)用,或者在很深的頁面才會調(diào)用,正常情況下很少會使用到,如果在啟動時加載了,就會造成內(nèi)存浪費。
getter ?也可使用懶加載方式
在dyld調(diào)用靜態(tài)構(gòu)造函數(shù)之前,libc 會調(diào)用 _objc_init()
objc_init會調(diào)用map_images 方法中-- > 會調(diào)用 read_images方法
read_images ?中會處理分類,把分類加到類上(方法,屬性等),然后進(jìn)行類的加載處理調(diào)用 load 方法等操作。
參考:
load和initialize方法的區(qū)別是什么? - 簡書
徹底搞懂+load和+initialize_康小曹(簡書)-CSDN博客