OC中的load和initialize方法

load

load方法在這個(gè)文件被程序裝載時(shí)調(diào)用(這個(gè)類被加入內(nèi)存的時(shí)候調(diào)用),這與這個(gè)類是否被用到無關(guān),因此load方法總是在main函數(shù)之前調(diào)用。

如果一個(gè)類實(shí)現(xiàn)了load方法,在調(diào)用這個(gè)方法前會(huì)首先調(diào)用父類的load方法。而且這個(gè)過程是自動(dòng)完成的,并不需要我們手動(dòng)實(shí)現(xiàn):


// In Parent.m

+ (void)load {

NSLog(@"Load Class Parent");

}

// In Child.m,繼承自Parent

+ (void)load {

NSLog(@"Load Class Child");

}

// In Child+load.m,Child類的分類

+ (void)load {

NSLog(@"Load Class Child+load");

}

// 運(yùn)行結(jié)果:

/*

2016-02-28 10:32:25.284 load[11789:1435378] Load Class Parent

2016-02-28 10:32:25.284 load[11789:1435378] Load Class Child

2016-02-28 10:32:25.284 load[11789:1435378] Load Class Child+load

*/



如果一個(gè)類沒有實(shí)現(xiàn)load方法,那么就不會(huì)調(diào)用它父類的load方法,這一點(diǎn)與正常的類繼承和方法調(diào)用不一樣,需要額外注意一下。


load方法調(diào)用時(shí),系統(tǒng)處于脆弱狀態(tài),如果調(diào)用別的類的方法,且該方法依賴于那個(gè)類的load方法進(jìn)行初始化設(shè)置,那么必須確保那個(gè)類的load方法已經(jīng)調(diào)用了,比如demo中的這段代碼,打印出的字符串就為null:


// In Child.m

+ (void)load {

NSLog(@"Load Class Child");

Other *other = [Other new];

[other originalFunc];

// 如果不先調(diào)用other的load,下面這行代碼就無效,打印出null

[Other printName];

}


案例

由于調(diào)用load方法時(shí)的環(huán)境很不安全,我們應(yīng)該盡量減少load方法的邏輯。另一個(gè)原因是load方法是線程安全的,它內(nèi)部使用了鎖,所以我們應(yīng)該避免線程阻塞在load方法中。一個(gè)常見的使用場景是在load方法中實(shí)現(xiàn)Method Swizzle:

// In Other.m

+ (void)load {

Method originalFunc = class_getInstanceMethod([self class], @selector(originalFunc));

Method swizzledFunc = class_getInstanceMethod([self class], @selector(swizzledFunc));

method_exchangeImplementations(originalFunc, swizzledFunc);

}

在Child類的load方法中,由于還沒調(diào)用Other的load方法,所以輸出結(jié)果是"Original Output",而在main函數(shù)中,輸出結(jié)果自然就變成了"Swizzled Output"。

一般來說,除了Method Swizzle,別的邏輯都不應(yīng)該放在load方法中實(shí)現(xiàn)。


initialize

與load方法類似的是,在initialize方法內(nèi)部也會(huì)調(diào)用父類的方法,而且不需要我們顯示的寫出來。與load方法不同之處在于,即使子類沒有實(shí)現(xiàn)initialize方法,也會(huì)調(diào)用父類的方法,這會(huì)導(dǎo)致一個(gè)很嚴(yán)重的問題:


// In Parent.m

+ (void)initialize {

NSLog(@"Initialize Parent, caller Class %@", [self class]);

}

// In Child.m

// 注釋掉initialize方法

// In main.m

Child *child = [Child new];

運(yùn)行后發(fā)現(xiàn)父類的initialize方法竟然調(diào)用了兩次:

2016-02-28 12:11:25.163 load[12772:1509345] Initialize Parent, caller Class Parent

2016-02-28 12:11:25.163 load[12772:1509345] Initialize Parent, caller Class Child


這是因?yàn)樵趧?chuàng)建子類對(duì)象時(shí),首先要?jiǎng)?chuàng)建父類對(duì)象,所以會(huì)調(diào)用一次父類的initialize方法,然后創(chuàng)建子類時(shí),盡管自己沒有實(shí)現(xiàn)initialize方法,但還是會(huì)調(diào)用到父類的方法。

雖然initialize方法對(duì)一個(gè)類而言只會(huì)調(diào)用一次,但這里由于出現(xiàn)了兩個(gè)類,所以調(diào)用兩次符合規(guī)則,但不符合我們的需求。正確使用initialize方法的姿勢(shì)如下:

// In Parent.m

+ (void)initialize {

if (self == [Parent class]) {

NSLog(@"Initialize Parent, caller Class %@", [self class]);

}

}

加上判斷后,就不會(huì)因?yàn)樽宇惗{(diào)用到自己的initialize方法了。

案例

initialize方法主要用來對(duì)一些不方便在編譯期初始化的對(duì)象進(jìn)行賦值,比如NSMutableArray這種類型的實(shí)例化依賴于runtime的消息發(fā)送,所以顯然無法在編譯器初始化:


// In Parent.m

static int someNumber = 0; ? ? // int類型可以在編譯期賦值

static NSMutableArray *someObjects;

+ (void)initialize {

if (self == [Parent class]) {

// 不方便編譯期復(fù)制的對(duì)象在這里賦值

someObjects = [[NSMutableArray alloc] init];

}

}


總結(jié)

load和initialize方法都會(huì)在實(shí)例化對(duì)象之前調(diào)用,以main函數(shù)為界,前者在main函數(shù)之前調(diào)用,后者在之后調(diào)用。這兩個(gè)方法會(huì)被自動(dòng)調(diào)用,不能手動(dòng)調(diào)用它們。

load和initialize方法都不用顯示的調(diào)用父類的方法而是自動(dòng)調(diào)用,即使子類沒有initialize方法也會(huì)調(diào)用父類的方法,而load方法則不會(huì)調(diào)用父類。

load方法通常用來進(jìn)行Method Swizzle,initialize方法一般用于初始化全局變量或靜態(tài)變量。

load和initialize方法內(nèi)部使用了鎖,因此它們是線程安全的。實(shí)現(xiàn)時(shí)要盡可能保持簡單,避免阻塞線程,不要再使用鎖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • OC中有兩個(gè)特殊的類方法,分別是load和initialize。本文總結(jié)一下這兩個(gè)方法的區(qū)別于聯(lián)系、使用場景和注意...
    RobinYu閱讀 457評(píng)論 0 3
  • load,再啟動(dòng)之前調(diào)用 1:父類 2:子類 3:分類 根據(jù)函數(shù)地址調(diào)用 initialize,在初次使用類對(duì)象的...
    哈哈哈我的簡書賬號(hào)閱讀 454評(píng)論 1 1
  • OC中有兩個(gè)特殊的類方法,分別是load和initialize。本文總結(jié)一下這兩個(gè)方法的區(qū)別于聯(lián)系、使用場景和注意...
    凱旋之歌閱讀 1,863評(píng)論 2 2
  • 注:本人是轉(zhuǎn)載供自己總結(jié)用, 做個(gè)筆記。 load方法。 在main函數(shù)前調(diào)用,如果一個(gè)類實(shí)現(xiàn)了load方法。在調(diào)...
    Fs_purple閱讀 388評(píng)論 0 4
  • 坐在烏鎮(zhèn)水邊的長椅上,眼前是落日下的余暉,晚霞在河道的那頭,我在河道的這頭,幾株樹影,暮色垂臨,很想就這樣靠著發(fā)一...
    影秋千閱讀 553評(píng)論 0 5

友情鏈接更多精彩內(nèi)容