調(diào)用機(jī)制
load方法的本質(zhì):直接執(zhí)行函數(shù)指針
load方法是在運(yùn)行時(shí)被執(zhí)行的(main函數(shù)之前)土砂,其調(diào)用棧如下
_ load_images
//加載類和類別的load方法
└── load_images_nolock
//執(zhí)行所有l(wèi)oad方法
└── call_load_methods
而在load_images_nolock
方法中旧蛾,則調(diào)用了prepare_load_methods
,其執(zhí)行了兩個(gè)方法:
_ prepare_load_methods
//先將需要執(zhí)行 load 的 class 添加到一個(gè)全局列表里 (loadable_class)
└── schedule_class_load
//然后將需要執(zhí)行 load 的 category 添加到另一個(gè)全局列表里(loadable_category)
└── add_category_to_loadable_list
而在shedule_class_load
方法中,確保先將父類添加到列表中矾缓。
static void schedule_class_load(class_t *cls)
{
assert(isRealized(cls)); // _read_images should realize
if (cls->data->flags & RW_LOADED) return;
//確保先將父類添加到全局列表里 (loadable_class)
class_t *supercls = getSuperclass(cls);
if (supercls) schedule_class_load(supercls);
//再將當(dāng)前類添加到全局列表里 (loadable_class)
add_class_to_loadable_list((Class)cls);
changeInfo(cls, RW_LOADED, 0);
}
然后再執(zhí)行call_load_methods
方法時(shí)
_ call_load_methods
//先遍歷 loadable_classes 列表中的類,執(zhí)行 load 方法稻爬。
└── call_class_loads
//然后再遍歷 loadable_category 列表中的分類 嗜闻,執(zhí)行 load 方法。
└── call_category_loads
而在call_class_loads
中因篇,執(zhí)行l(wèi)oad方法的代碼為
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
IMP load_method = classes[i].method;
if (!cls) continue;
if (PrintLoading) {
_objc_inform("LOAD: +[%s load]\n", _class_getName(cls));
}
(*load_method) ((id) cls, SEL_load);
}
由以上可知泞辐,load
方法笔横,其實(shí)就是直接執(zhí)行函數(shù)指針竞滓,不會(huì)執(zhí)行消息發(fā)送objc_msgSend
那一套流程。子類吹缔、分類的load
方法不會(huì)覆蓋父類的load
方法商佑。
initialize方法的本質(zhì)
在類、或者子類厢塘,接收到第一條消息之前被執(zhí)行(如初始化)
initialize
方法最終通過objc_msgSend
來執(zhí)行
initialize方法在main函數(shù)之后調(diào)用
如果一直沒有使用類茶没,則initialize
方法不會(huì)被調(diào)用
如果子類沒有實(shí)現(xiàn)initialize
方法,則會(huì)調(diào)用父類的initialize
方法
源碼分析:
__private_extern__ void _class_initialize(Class cls)
{
Class supercls;
BOOL reallyInitialize = NO;
// Get the real class from the metaclass. The superclass chain
// hangs off the real class only.
cls = _class_getNonMetaClass(cls);
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
supercls = _class_getSuperclass(cls);
if (supercls && !_class_isInitialized(supercls)) {
_class_initialize(supercls);
}
// Try to atomically set CLS_INITIALIZING.
monitor_enter(&classInitLock);
if (!_class_isInitialized(cls) && !_class_isInitializing(cls)) {
_class_setInitializing(cls);
reallyInitialize = YES;
}
monitor_exit(&classInitLock);
if (reallyInitialize) {
// We successfully set the CLS_INITIALIZING bit. Initialize the class.
// Record that we're initializing this class so we can message it.
_setThisThreadIsInitializingClass(cls);
// Send the +initialize message.
// Note that +initialize is sent to the superclass (again) if
// this class doesn't implement +initialize. 2157218
if (PrintInitializing) {
_objc_inform("INITIALIZE: calling +[%s initialize]",
_class_getName(cls));
}
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
if (PrintInitializing) {
_objc_inform("INITIALIZE: finished +[%s initialize]",
_class_getName(cls));
}
// Done initializing.
......
}
優(yōu)先執(zhí)行父類的initialize
方法晚碾;通過_class_getSupercass
取出父類抓半,遞歸調(diào)用父類的initialize
方法;initialize
方法最終通過objc_msgSend
來執(zhí)行的格嘁。
執(zhí)行順序
load
- 先調(diào)用類的
load
笛求,再調(diào)用分類的load
- 先編譯的類,優(yōu)先調(diào)用
load
糕簿,調(diào)用子類的load
之前探入,會(huì)先調(diào)用父類的load
- 先編譯的分類,優(yōu)先調(diào)用
load
懂诗,順序和Compile Sources
中順序一致
場(chǎng)景1 :子類蜂嗽、父類、分類都實(shí)現(xiàn)load方法殃恒,調(diào)用情況
答:SuperClass->SubClass->CategoryClass
場(chǎng)景2 :子類植旧、父類、分類中子類不實(shí)現(xiàn)load方法离唐,調(diào)用情況
答:SuperClass->CategoryClass
場(chǎng)景3 :子類病附、父類、分類1侯繁、分類2都實(shí)現(xiàn)load方法胖喳,調(diào)用情況
答:SuperClass->SubClass->Category1Class->Category2Class
initialize
- 父類先于子類執(zhí)行;(同load方法)
- 子類未實(shí)現(xiàn)贮竟,則會(huì)調(diào)用父類的
initialize
方法丽焊; - 分類實(shí)現(xiàn)了
initialize
方法较剃,則會(huì)覆蓋類中的initialize
方法(同category); - 存在多個(gè)分類技健,依賴
Compile Sources
中的順序写穴,執(zhí)行最后一個(gè)分類的initialize
方法(同category);
場(chǎng)景1 :子類雌贱、父類都實(shí)現(xiàn)initialize方法啊送,調(diào)用情況
答:SuperClass->SubClass
場(chǎng)景2 :子類、父類中子類不實(shí)現(xiàn)initialize方法欣孤,調(diào)用情況
答:SuperClass->SuperClass
(子類未實(shí)現(xiàn)馋没,則會(huì)調(diào)用父類的initialize
,導(dǎo)致父類調(diào)用多次)
場(chǎng)景3:子類降传、父類篷朵、子類分類都實(shí)現(xiàn)initialize方法,調(diào)用情況
答:SuperClass->CategoryClass
(category中initialize方法覆蓋其本類)
場(chǎng)景4:子類婆排、父類声旺、父類分類1、父類分類2都實(shí)現(xiàn)initialize方法段只,調(diào)用情況
答:CategoryClass->SubClass
(category中initialize方法根據(jù)Compile Sources排序執(zhí)行最后一個(gè))
使用場(chǎng)景
-
load
通常用于Method Swizzle
腮猖; -
initialize
可以用于初始化全局變量或靜態(tài)變量;
注意:load和initialize方法內(nèi)部使用了鎖赞枕,因此他們是線程安全的澈缺。使用時(shí)避免阻塞線程,不要使用線程鎖鹦赎。
面試題
1谍椅、runtime中的交換方法在initialize中實(shí)現(xiàn)會(huì)有什么問題?
答:initialize
方法可能被其分類中的initialize方法覆蓋古话,導(dǎo)致無法調(diào)用雏吭。