[toc]
參考
http://www.reibang.com/p/a358a397a4ce
http://blog.csdn.net/ShengQiangLiu/article/details/50866228
code
// NSObject 有實(shí)現(xiàn)該方法
- (void)load;
objc4源碼解讀
// objc-os.mm
_objc_init();
// objc-runtime-new.mm
load_images();
// 準(zhǔn)備(查找所有被實(shí)現(xiàn)的load方法)
prepare_load_methods(); // Discover load methods
/// 類 ★
// 按(編譯)順序加載 classlist 數(shù)組中的類, 這個(gè)順序就是 BuildPhases 中 CompileSources 的順序
classref_t const *classlist = _getObjc2NonlazyClassList(mhdr, &count);
schedule_class_load(remapClass(classlist[i])); // for循環(huán)
// ★★ 遞歸調(diào)用, 傳入super, 所以父類早于子類添加到數(shù)組中, 保證父類早于子類load
schedule_class_load(cls->superclass);
// ★★ 將cls添加到 loadable_classes 數(shù)組的最后面, call_class_loads()是從前往后遍歷, 所以后添加的后加載;
add_class_to_loadable_list();
method = cls->getLoadMethod(); // 獲取 load 的IMP
if (!method) return; // ★★ 如果類沒(méi)有實(shí)現(xiàn) +load, 則不加入數(shù)組, 后面也不會(huì)去調(diào)用
loadable_classes[loadable_classes_used].cls = cls;
loadable_classes[loadable_classes_used].method = method;
/// 分類 ★
// 按(編譯)順序加載 categorylist 數(shù)組中的分類
category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
// for循環(huán), 將 cat 添加到 loadable_categories
// 注意, 分類這里沒(méi)有遞歸調(diào)用, 不用管 super
add_category_to_loadable_list();
method = _category_getLoadMethod(cat);
if (!method) return;
loadable_categories[loadable_categories_used].cat = cat;
loadable_categories[loadable_categories_used].method = method;
// 加載
call_load_methods(); // objc-loadmethod.mm
// 先調(diào)用所有類的load
call_class_loads();
// 從上面prepare好的 loadable_classes 數(shù)組中取出所有類
struct loadable_class *classes = loadable_classes;
// for循環(huán)該數(shù)組, 取到類的load方法的內(nèi)存地址, 賦值給函數(shù)指針load_method
load_method_t load_method = (load_method_t)classes[i].method;
// 使用函數(shù)指針, 直接調(diào)用每一個(gè)類的load方法
(*load_method)(cls, @selector(load));
// 再調(diào)用所有分類的load
call_category_loads();
load_method_t load_method = (load_method_t)cats[i].method;
(*load_method)(cls, @selector(load));
結(jié)論★★:
-
系統(tǒng)調(diào)用
+load
方法是根據(jù)方法地址直接調(diào)用, 并不是經(jīng)過(guò)objc_msgSend
函數(shù)調(diào)用。- 所以, 所有類的已實(shí)現(xiàn)的load 都會(huì)被調(diào)用 (未實(shí)現(xiàn)則不會(huì)調(diào)用, 也不會(huì)去調(diào)用父類的)咒锻。
- 主類的 load 方法, 并不會(huì)被分類覆蓋蝗罗。
-
先調(diào)用類的
+load
- 各個(gè)類之間, 按照編譯先后順序調(diào)用 (先編譯所袁,先調(diào)用)星立。
- 調(diào)用子類的
+ load
之前會(huì)先調(diào)用父類的+ load
-
再調(diào)用分類的
+ load
(分類的 load 是在所有主類 load 完畢之后才調(diào)用)。- 各個(gè)分類之間, 按照編譯先后順序調(diào)用 (先編譯勾拉,先調(diào)用)粘姜。
調(diào)用時(shí)機(jī):
+ load
方法會(huì)在runtime加載類、分類時(shí)調(diào)用 [TBC ??? load 是 runtime 調(diào)用的嗎]
程序一啟動(dòng), 在main()
函數(shù)執(zhí)行之前, 當(dāng)類或分類被加載到內(nèi)存時(shí)被調(diào)用乞榨。
換句話說(shuō), 這個(gè)load方法在 didFinishLaunchingWithOptions 之前就被調(diào)用了;
《Apple Document》
Invoked whenever a class or category is added to the Objective-C runtime; implement this method to perform class-specific behavior upon loading.
調(diào)用次數(shù):
每個(gè)類秽之、分類的+ load
,在程序運(yùn)行過(guò)程中, 默認(rèn)會(huì)且只會(huì)執(zhí)行一次
調(diào)用必然性:
必然調(diào)用, 不管程序運(yùn)行過(guò)程中有沒(méi)有用到這個(gè)類, 都會(huì)調(diào)用load方法 (如果有實(shí)現(xiàn))
調(diào)用順序:
同一繼承體系下, 先加載父類, 再加載子類, 然后再加載子類的分類(按編譯順序, 先編譯先調(diào)用);
不同的類之間的加載順序: 是不確定的 按照編譯先后順序調(diào)用(先編譯, 先調(diào)用)吃既。
《Apple Document》
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.
見(jiàn)【objc4源碼解讀 - 結(jié)論】
父類
父類先于子類加載, 子類不要手動(dòng)調(diào)用 [super load]
, 否則父類的load
會(huì)執(zhí)行多次考榨。
load 不遵循繼承規(guī)則, 不管子類有沒(méi)有寫(xiě)load方法, 都不會(huì)去查找調(diào)用父類的load
分類
與其他方法不同, 每個(gè)類的load都是獨(dú)立的, 不存在繼承、重寫(xiě), 在Category中重寫(xiě)load函數(shù)不會(huì)替換原始類中的load, 原始類和Category中的load函數(shù)都會(huì)被執(zhí)行, 原始類的load會(huì)先被執(zhí)行, 再執(zhí)行Category中的load函數(shù)鹦倚。
當(dāng)有多個(gè) Category 都實(shí)現(xiàn)了load函數(shù), 這幾個(gè)load函數(shù)都會(huì)執(zhí)行, 按編譯順序, 先編譯先調(diào)用河质。
調(diào)用方式:
系統(tǒng)自動(dòng)調(diào)用, 不要手動(dòng)調(diào)用 (但實(shí)際也能調(diào)用)
安全性
線程安全 內(nèi)部加鎖 線程阻塞
在load方法中使用其他類是不安全的, 因?yàn)闀?huì)調(diào)用其他類的load方法, 而如果關(guān)系復(fù)雜的話, 就無(wú)法判斷出各個(gè)類的載入順序, 類只有初始化完成后, 類實(shí)例才能進(jìn)行正常使用
盡可能的精簡(jiǎn)load方法, 因?yàn)檎麄€(gè)應(yīng)用程序在執(zhí)行l(wèi)oad方法時(shí)會(huì)阻塞, 即, 程序會(huì)阻塞直到所有類的load方法執(zhí)行完畢, 才會(huì)繼續(xù)
應(yīng)用場(chǎng)景
一般的應(yīng)用場(chǎng)景是在該方法中實(shí)現(xiàn)方法交換(Method Swizzling)
面試題
Category中有l(wèi)oad方法嗎?load方法是什么時(shí)候調(diào)用的震叙?
有l(wèi)oad方法
load方法在runtime加載類掀鹅、分類的時(shí)候調(diào)用 【TBC】
load 方法能繼承嗎?★★
-
答案
load 方法可以繼承媒楼,但是一般情況下不會(huì)主動(dòng)去調(diào)用load方法乐尊,都是讓系統(tǒng)自動(dòng)調(diào)用。
-
驗(yàn)證:
先創(chuàng)建繼承自 NSObject 的 QGPerson, 實(shí)現(xiàn) +load 方法, 添加打印代碼;
然后創(chuàng)建繼承自 QGPerson 的 QGStudent, 不實(shí)現(xiàn) +load 方法;
然后在
main()
函數(shù)中, 手動(dòng)調(diào)用[QGStudent load];
會(huì)發(fā)現(xiàn)
QGPerson
的+load
在main()
前后被調(diào)用了2次匣砖。可見(jiàn) +load 是存在繼承的, 如果自己沒(méi)有實(shí)現(xiàn), 可以沿著super_class調(diào)用父類的科吭。
-
解析:
首先,
[QGStudent load];
這樣寫(xiě)就是消息發(fā)送機(jī)制, 相當(dāng)于objc_msgSend([QGStudent class], @selector(load));
會(huì)根據(jù) QGStudent 的 isa 找到其元類對(duì)象, 在其元類對(duì)象中查找 load, 找不到, 再根據(jù) super_class 找到父元類對(duì)象, 從而找到并調(diào)用了父類的 +load。
load猴鲫、initialize的區(qū)別对人?
-
調(diào)用方式
load 是根據(jù)函數(shù)地址直接調(diào)用
initialize 是通過(guò) objc_msgSend 調(diào)用
-
調(diào)用時(shí)刻
load 是 runtime 加載類、分類的時(shí)候調(diào)用(只會(huì)調(diào)用1次)
initialize 是類第一次接收到消息的時(shí)候調(diào)用, 每一個(gè)類只會(huì) initialize 一次(父類的initialize方法可能會(huì)被調(diào)用多次)
load拂共、initialize 的調(diào)用順序牺弄?
-
load
【見(jiàn)本文- 源碼 - 結(jié)論】
-
先初始化父類
再初始化子類(可能最終調(diào)用的是父類的initialize方法)
load、initialize 在category中的調(diào)用的順序宜狐?
load势告、initialize 出現(xiàn)繼承時(shí)他們之間的調(diào)用過(guò)程蛇捌?
系統(tǒng)是怎么調(diào)用 load 方法的?
不是通過(guò)消息機(jī)制, 而是直接通過(guò)函數(shù)指針調(diào)用