它們的相同點在于:
方法只會被調(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í)行一個 |