24·iOS 面試題·+(void)load; +(void)initialize; 有什麼用處?
前言
寫在最前面,Objective-C +load vs +initialize 這篇部落格已經非常完美的回答了這道題目,我這篇文章就是學習完之後的學習筆記。強烈建議大家去閱讀 Objective-C +load vs +initialize 。
+load 方法
+load 方法呼叫時機
+load 方法是當類被新增 Objective-C Runtime 時呼叫的,類呼叫順序如下:
- 父類的 +load 方法
- 子類的 +load 方法
- 分類的 +load 方法(有多個分類,呼叫順序是看分類檔案的編譯順序)
+load 方法被呼叫的方式
下面是 runtime 中,呼叫 +load 方法最核心的原始碼:
static void call_class_loads(void)
{
int i;
// Detach current loadable list.
struct loadable_class *classes = loadable_classes;
int used = loadable_classes_used;
loadable_classes = nil;
loadable_classes_allocated = 0;
loadable_classes_used = 0;
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
load_method_t load_method = (load_method_t)classes[i].method;
if (!cls) continue;
if (PrintLoading) {
_objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
}
//核心呼叫方式
(*load_method)(cls, SEL_load);
}
// Destroy the detached list.
if (classes) _free_internal(classes);
}
通過原始碼可以看到,呼叫方式是 (*load_method)(cls, SEL_load);
,這裡是直接使用函式記憶體地址方式呼叫,而不是用 objc_masSend
發訊息的機制;
直接利用函式地址呼叫的方法,沒有走訊息轉發機制,就會出現這樣子的現象:父類、子類、分類的 +load 互不影響,假設子類沒有實現 +load 方法,也不會去呼叫多一次父類的 +load 方法。
+load 方法可以做什麼
由於 +load 方法是在 Runtime 載入類時呼叫的,執行時機是比較早的,在這裡我們可以做 Method Swizzling,對一些函式進行 Hook。(無痕埋點、AOP 都可以利用這個機制來實現)
+initialize 方法
+initialize 方法呼叫時機
+initialize 方法是在類或它的子類收到第一條訊息之前被呼叫的,runtime 原始碼如下:
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
...
rwlock_unlock_write(&runtimeLock);
}
if (initialize && !cls->isInitialized()) {
_class_initialize (_class_getNonMetaClass(cls, inst));
// If sel == initialize, _class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen. 2778172
}
// The lock is held to make method-lookup + cache-fill atomic
// with respect to method addition. Otherwise, a category could
...
}
當給一個類傳送訊息的時候,runtime 會呼叫 lookUpImpOrForward
函式,這裡會對沒有初始過的類進行初始化,呼叫 _class_initialize
函式。
+initialize 方法被呼叫的方式
下面是 runtime 中,呼叫 +initialize 方法最核心的原始碼:
void _class_initialize(Class cls)
{
...
Class supercls;
BOOL reallyInitialize = NO;
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
// Try to atomically set CLS_INITIALIZING.
monitor_enter(&classInitLock);
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
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]",
cls->nameForLogging());
}
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
if (PrintInitializing) {
_objc_inform("INITIALIZE: finished +[%s initialize]",
...
}
通過這個函式,我們可以知道兩點:
- 遞迴呼叫 +initialize 方法的,父類比子類先初始化
- 利用
objc_msgSend
發訊息模式呼叫 +initialize 方法的(這裡與其它普通方法呼叫一致)
故我們可以知道,如果子類沒有實現 +initialize 方法,那麼繼承自父類的實現會被呼叫;如果一個類的分類實現了 +initialize 方法,那麼就會對這個類中的實現造成覆蓋。
+initialize 方法可以做什麼
+initialize 方法呼叫的時機也是非常早的,並且是懶載入模式,可以在 +initialize 方法裡面做一些初始化的工作。
+load 方法 和 +initialize 方法比較
這裡直接用 Objective-C +load vs +initialize 中的總結:
+load | +initialize | |
---|---|---|
呼叫時機 | 被新增到 runtime 時 | 收到第一條訊息前,可能永遠不呼叫 |
呼叫順序 | 父類->子類->分類 | 父類->子類 |
呼叫次數 | 1次 | 多次 |
是否需要顯式呼叫父類實現 | 否 | 否 |
是否沿用父類的實現 | 否 | 是 |
分類中的實現 | 類和分類都執行 | 覆蓋類中的方法,只執行分類的實現 |
總結
通過這個面試題,在網上找到了這篇部落格:Objective-C +load vs +initialize ,又學習到知識點了,開心。
參考文獻
Objective-C +load vs +initialize
06·iOS 面試題·Category 中有 load 方法嗎?load 方法是什麼時候呼叫的?load 方法能繼承嗎?
相關文章
- NSObject +(void)load 和+(void)initialize 方法的理解Object
- 為什麼用「void 0」代替「undefined」Undefined
- 你知道void和Void的區別嗎
- a===void 0 作用
- a標籤下的href="javascript:void(0)"起到了什麼作用?說說你對javascript:void(0)的理解?JavaScript
- JavaScript void 運算子JavaScript
- TypeScript void 型別TypeScript型別
- iOS面試題-load 和 initlize的區別iOS面試題
- load與initialize
- 為什麼Java的main方法必須是public static void?JavaAI
- 你真的知道為什麼要使用void(0)代替undefined嗎?Undefined
- [Javascript] Replace undefined with void 0JavaScriptUndefined
- typedef void (*Fun) (void) 的理解——函式指標——typedef函式指標函式指標
- int存放到void指標指標
- C 語言中 void* 詳解及應用介紹
- C++ const void * 型別轉換簡單測試C++型別
- 請解釋下href="javascript:void(0)"和href="#"的區別是什麼?JavaScript
- 行內元素、塊級元素、空(void)元素分別有哪些?
- Runtime原始碼 +load 和 +initialize原始碼
- NSObject 的 initialize 和 load 方法Object
- ObjC load與initialize 簡析OBJ
- 前端戰五渣學JavaScript——void 運算子前端JavaScript
- Java的Void方法是反模式的? - DZoneJava模式
- iOS面試題iOS面試題
- initialize方法與load方法比較
- +load和+initialize方法呼叫時機
- 【java面試】Spring的IOC是啥?有什麼好處?Java面試Spring
- OC中的SEL與C中的const void *
- Java培訓分享void的用法和意義Java
- Java技術分享:void的用法和意義Java
- 【JavaScript】奇怪的知識void 0 === undefined 為 trueJavaScriptUndefined
- 測試面試題:cookie/session/token 分別表示什麼意思,有什麼區別?面試題CookieSession
- Category的本質<二>load,initialize方法Go
- iOS 面試問題iOS面試
- iOS Runloop(面試題)iOSOOP面試題
- 面試題:MySQL索引為什麼用B+樹?面試題MySql索引
- 常見面試題:java8有什麼新特性?面試題Java
- 面試題: Webpack 的 plugin 和 loader 有什麼區別面試題WebPlugin