load和initialize的區(qū)別

演示代碼1下載地址
演示代碼2下載地址

它們的相同點在于:

方法只會被調(diào)用一次(其實這是相對runtime來說的,后邊會做進一步解釋)。

Apple的文檔很清楚地說明了initialize和load的區(qū)別在于:

load是只要類所在文件被引用就會被調(diào)用,而initialize是在類或者其子類的第一個方法被調(diào)用前調(diào)用。所以如果類沒有被引用進項目,就不會有l(wèi)oad調(diào)用;但即使類文件被引用進來,但是沒有使用,那么initialize也不會被調(diào)用。

總結(jié):

+(void)load +(void)initialize
執(zhí)行時機 在程序運行后立即執(zhí)行 在類的方法第一次被調(diào)時執(zhí)行
若自身未定義,是否沿用父類的方法?
類別中的定義 全都執(zhí)行,但后于類中的方法 覆蓋類中的方法,只執(zhí)行一個

文檔也明確闡述了方法調(diào)用的順序:

父類(Superclass)的方法優(yōu)先于子類(Subclass)的方法,類中的方法優(yōu)先于類別(Category)中的方法。
不過還有很多地方是文章中沒有解釋詳細的。所以再來看一些示例代碼來明確其中應(yīng)該注意的細節(jié),如下所示,創(chuàng)建三個類。

示例代碼1

@interface SuperClass : NSObject

@end

@implementation SuperClass

+ (void) initialize {
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

+ (void) load {
    NSLog(@"%@ %s", [self class], __FUNCTION__);
    
}

@end
@interface ChildClass : SuperClass

@end

@implementation ChildClass

+ (void) initialize {
    NSLog(@"%@ %s", [self class], __FUNCTION__);
    Insideinitialize * obj = [[Insideinitialize alloc] init];
    [obj objectMethod];
//    [obj release];
}
@end
@interface Insideinitialize : NSObject
- (void)objectMethod;
@end

@implementation Insideinitialize
- (void)objectMethod {
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

+ (void) initialize {
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

+ (void) load {
    NSLog(@"%s", __FUNCTION__);
}
@end

這個示例代碼中,一個SuperClass實現(xiàn)了+(void)load和+(void)initialize方法(實際上應(yīng)該算是重寫覆蓋了NSObject的這兩個方法);ChildClass繼承于SuperClass,但是只重寫+(void)initialize沒有+(void)load;Insideinitialize類也有+(void)load和+(void)initialize方法,它在ChildClass的i+(void)initialize方法中被構(gòu)建出一個對象。類中的每個函數(shù)的實現(xiàn)都非常簡單,只是輸出類名和方法名。除了Insideinitialize的+(void)load方法只輸出了類名,沒有使用[self class]。

首先我們在Xcode的項目中只簡單import這些類,而不去使用他們的,然后運行項目就會得到下邊的結(jié)果

SuperClass +[SuperClass initialize]
SuperClass +[SuperClass load]
Insideinitialize +[Insideinitialize load]

就像Apple的文檔中說的一下,只要有引用runtime就會自動去調(diào)用類的+(void)load方法。不過從輸出中,我們還發(fā)現(xiàn)SuperClass的+(void)initialize也被調(diào)用了,而且是在+(void)load之前被執(zhí)行;而Insideinitialize的+(void)initialize并沒有執(zhí)行。這是因為在SuperClass的+(void)load方法中,我們調(diào)用了類的class方法([self class]),這就符合文檔中對+(void)initialize的說明:在類的第一個方法被調(diào)用前調(diào)用。同時也說明runtime對+(void)load的調(diào)用并不視為類的第一個方法。而ChildClass因為沒有用到,所以+(void)initialize的方法被沒有被執(zhí)行,而且它也沒有去執(zhí)行父類的+(void)load方法(雖然它有繼承下該方法)

+(void)load和+(void)initialize可當(dāng)做普通類方法(Class Method)被調(diào)用

接著, 在程序中讓ChildClass直接調(diào)用load:

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [ChildClass load];
}

@end

[ChildClass load];
程序正常運行,并輸出了結(jié)果:

 SuperClass +[SuperClass initialize]
SuperClass +[SuperClass load]
 +[Insideinitialize load]
 ChildClass +[ChildClass initialize]
 Insideinitialize +[Insideinitialize initialize]
Insideinitialize -[Insideinitialize objectMethod]
 ChildClass +[SuperClass load]

前面三個結(jié)果跟之前一樣,不過之后ChildClass的+(void)initialize也被自動執(zhí)行調(diào)用,并且我們可以在其中安全創(chuàng)建出Insideinitialize類并使用它,而Insideinitialize因為調(diào)用alloc方法是第一次使用類方法, 所以激發(fā)了Insideinitialize的+(void)initialize。

另一個方面,ChildClass繼承下了+(void)load而且可以被安全地當(dāng)做普通類方法(Class Method)被使用。這也就是我之前所說的load和initialize被調(diào)用一次是相對runtime而言(比如SuperClass的initialize不會因為自身load方法調(diào)用一次,又因為子類調(diào)用了load又執(zhí)行一次),我們依然可以直接去反復(fù)調(diào)用這些方法。
子類會調(diào)用父類的+(void)initialize

接下來,我們再修改一下SuperClass和ChildClass:去掉SuperClass中的+(void)load方法;讓ChildClass來重寫+(void)load,但是去掉+(void)initialize。

SuperClass +[SuperClass initialize]
ChildClass +[SuperClass initialize]
ChildClass +[ChildClass load]

和之前一樣,+(void)load會引起+(void)initialize。也很Apple文檔中講得那樣,子類方法的調(diào)用會激起父類的+(void)initialize被執(zhí)行。不過我們也看到雖然ChildClass沒有定義+(void)initialize,但是它會使用父類的+(void)initialize。而之前的示例,我們看到子類并不會在runtime時去使用父類的+(void)load,也就是說只有新定義的+(void)load才會被runtime去調(diào)用執(zhí)行。

類別(Category)中的+(void)load的+(void)initialize

我們再來看看類實現(xiàn)(@implementation)和類的類別(Category)中+(void)load和+(void)initialize的區(qū)別。

示例代碼2

/******* Interface *******/
@interface MainClass : NSObject
@end

/******* Category Implementation *******/
@implementation MainClass(Category)

+ (void) load {
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

+ (void) initialize {
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

@end

@implementation MainClass(OtherCategory)

+ (void) load {
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

+ (void) initialize {
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

@end

/******* Implementation *******/
@implementation MainClass

+ (void) load {
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

+ (void) initialize {
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

@end

簡單運行后的打印結(jié)果如下:

MainClass +[MainClass(OtherCategory) initialize]
MainClass +[MainClass load]
MainClass +[MainClass(Category) load]
MainClass +[MainClass(OtherCategory) load]

同樣的+(void)initialize優(yōu)先于+(void)load先執(zhí)行。但是很明顯的不同在于,只有最后一個類別(Category)的+(void)initialize執(zhí)行,其他兩個都被隱藏。而對于+(void)load,三個都執(zhí)行,并且如果Apple的文檔中介紹順序一樣:
先執(zhí)行類自身的實現(xiàn),再執(zhí)行類別(Category)中的實現(xiàn)。

Runtime調(diào)用+(void)load時沒有autorelease pool

最后再來看一個示例

@interface MainClass : NSObject
@end

@implementation MainClass

+ (void) load {
    NSArray *array = [NSArray array];
    NSLog(@"%@ %s", array, __FUNCTION__);
}

@end

運行這段代碼,Xcode給出如下的信息:

objc[84934]: Object 0x10a512930 of class __NSArrayI autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool() to debug
2012-09-28 18:07:39.042 ClassMethod[84934:403] (
) +[MainClass load]

其原因是runtime調(diào)用+(void)load的時候,程序還沒有建立其autorelease pool,所以那些會需要使用到autorelease pool的代碼,都會出現(xiàn)異常。這一點是非常需要注意的,也就是說放在+(void)load中的對象都應(yīng)該是alloc出來并且不能使用autorelease來釋放。

不需要顯示使用super調(diào)用父類中的方法

當(dāng)我們定義-(id)init和-(void)dealloc方法時,我們總是需要使用super關(guān)鍵字來調(diào)用父類的方法,讓父類也完成相同的操作。這是因為對對象的初始化和銷毀過程,Objective-C不像C++,C#那樣會自動調(diào)用父類默認(rèn)構(gòu)造函數(shù)。因此我們總是需要將這兩個函數(shù)寫成這樣:

- (id)init {
    if ((self = [super init])) {
        //do initialization
    }
    
    return self;
}

- (void)dealloc {
    //do release
    
    [super dealloc];
}

但是+(void)initialize和+(void)load不同,我們并不需要在這兩個方法的實現(xiàn)中使用super調(diào)用父類的方法:

+ (void)initialize {
    //do initialization thing
    [super initialize];
}

+ (void) load {
    //do some loading things
    [super load];
}

super的方法會成功調(diào)用,但是這是多余的,因為runtime對自動對父類的+(void)load方法進行調(diào)用,而+(void)initialize則會隨子類自動激發(fā)父類的方法(如Apple文檔中所言)不需要顯示調(diào)用。另一方面,如果父類中的方法用到的self(像示例中的方法),其指代的依然是類自身,而不是父類。

markdown表格制作方法:


|       Tables        |             +(void)load           |       +(void)initialize        |
| :-----------------: |:-----------------------:| :---------------------: |
|           執(zhí)行時機         |  在程序運行后立即執(zhí)行 | 在類的方法第一次被調(diào)時執(zhí)行 |
| 若自身未定義,是否沿用父類的方法?|         否       |            是      |
|      類別中的定義        | 全都執(zhí)行,但后于類中的方法  |  覆蓋類中的方法,只執(zhí)行一個 |
Tables +(void)load +(void)initialize
執(zhí)行時機 在程序運行后立即執(zhí)行 在類的方法第一次被調(diào)時執(zhí)行
若自身未定義,是否沿用父類的方法?
類別中的定義 全都執(zhí)行,但后于類中的方法 覆蓋類中的方法,只執(zhí)行一個
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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