它們的相同點在于:
方法只會被調用一次(其實這是相對runtime來說的,后邊會做進一步解釋)。
Apple的文檔很清楚地說明了initialize和load的區別在于:
load是只要類所在文件被引用就會被調用,而initialize是在類或者其子類的第一個方法被調用前調用。所以如果類沒有被引用進項目,就不會有load調用;但即使類文件被引用進來,但是沒有使用,那么initialize也不會被調用。
總結:
+(void)load | +(void)initialize | |
---|---|---|
執行時機 | 在程序運行后立即執行 | 在類的方法第一次被調時執行 |
若自身未定義,是否沿用父類的方法? | 否 | 是 |
類別中的定義 | 全都執行,但后于類中的方法 | 覆蓋類中的方法,只執行一個 |
文檔也明確闡述了方法調用的順序:
父類(Superclass)的方法優先于子類(Subclass)的方法,類中的方法優先于類別(Category)中的方法。
不過還有很多地方是文章中沒有解釋詳細的。所以再來看一些示例代碼來明確其中應該注意的細節,如下所示,創建三個類。
示例代碼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實現了+(void)load和+(void)initialize方法(實際上應該算是重寫覆蓋了NSObject的這兩個方法);ChildClass繼承于SuperClass,但是只重寫+(void)initialize沒有+(void)load;Insideinitialize類也有+(void)load和+(void)initialize方法,它在ChildClass的i+(void)initialize方法中被構建出一個對象。類中的每個函數的實現都非常簡單,只是輸出類名和方法名。除了Insideinitialize的+(void)load方法只輸出了類名,沒有使用[self class]。
首先我們在Xcode的項目中只簡單import這些類,而不去使用他們的,然后運行項目就會得到下邊的結果
SuperClass +[SuperClass initialize]
SuperClass +[SuperClass load]
Insideinitialize +[Insideinitialize load]
就像Apple的文檔中說的一下,只要有引用runtime就會自動去調用類的+(void)load方法。不過從輸出中,我們還發現SuperClass的+(void)initialize也被調用了,而且是在+(void)load之前被執行;而Insideinitialize的+(void)initialize并沒有執行。這是因為在SuperClass的+(void)load方法中,我們調用了類的class方法([self class]),這就符合文檔中對+(void)initialize的說明:在類的第一個方法被調用前調用。同時也說明runtime對+(void)load的調用并不視為類的第一個方法。而ChildClass因為沒有用到,所以+(void)initialize的方法被沒有被執行,而且它也沒有去執行父類的+(void)load方法(雖然它有繼承下該方法)
+(void)load和+(void)initialize可當做普通類方法(Class Method)被調用
接著, 在程序中讓ChildClass直接調用load:
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[ChildClass load];
}
@end
[ChildClass load];
程序正常運行,并輸出了結果:
SuperClass +[SuperClass initialize]
SuperClass +[SuperClass load]
+[Insideinitialize load]
ChildClass +[ChildClass initialize]
Insideinitialize +[Insideinitialize initialize]
Insideinitialize -[Insideinitialize objectMethod]
ChildClass +[SuperClass load]
前面三個結果跟之前一樣,不過之后ChildClass的+(void)initialize也被自動執行調用,并且我們可以在其中安全創建出Insideinitialize類并使用它,而Insideinitialize因為調用alloc方法是第一次使用類方法, 所以激發了Insideinitialize的+(void)initialize。
另一個方面,ChildClass繼承下了+(void)load而且可以被安全地當做普通類方法(Class Method)被使用。這也就是我之前所說的load和initialize被調用一次是相對runtime而言(比如SuperClass的initialize不會因為自身load方法調用一次,又因為子類調用了load又執行一次),我們依然可以直接去反復調用這些方法。
子類會調用父類的+(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文檔中講得那樣,子類方法的調用會激起父類的+(void)initialize被執行。不過我們也看到雖然ChildClass沒有定義+(void)initialize,但是它會使用父類的+(void)initialize。而之前的示例,我們看到子類并不會在runtime時去使用父類的+(void)load,也就是說只有新定義的+(void)load才會被runtime去調用執行。
類別(Category)中的+(void)load的+(void)initialize
我們再來看看類實現(@implementation)和類的類別(Category)中+(void)load和+(void)initialize的區別。
示例代碼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
簡單運行后的打印結果如下:
MainClass +[MainClass(OtherCategory) initialize]
MainClass +[MainClass load]
MainClass +[MainClass(Category) load]
MainClass +[MainClass(OtherCategory) load]
同樣的+(void)initialize優先于+(void)load先執行。但是很明顯的不同在于,只有最后一個類別(Category)的+(void)initialize執行,其他兩個都被隱藏。而對于+(void)load,三個都執行,并且如果Apple的文檔中介紹順序一樣:
先執行類自身的實現,再執行類別(Category)中的實現。
Runtime調用+(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調用+(void)load的時候,程序還沒有建立其autorelease pool,所以那些會需要使用到autorelease pool的代碼,都會出現異常。這一點是非常需要注意的,也就是說放在+(void)load中的對象都應該是alloc出來并且不能使用autorelease來釋放。
不需要顯示使用super調用父類中的方法
當我們定義-(id)init和-(void)dealloc方法時,我們總是需要使用super關鍵字來調用父類的方法,讓父類也完成相同的操作。這是因為對對象的初始化和銷毀過程,Objective-C不像C++,C#那樣會自動調用父類默認構造函數。因此我們總是需要將這兩個函數寫成這樣:
- (id)init {
if ((self = [super init])) {
//do initialization
}
return self;
}
- (void)dealloc {
//do release
[super dealloc];
}
但是+(void)initialize和+(void)load不同,我們并不需要在這兩個方法的實現中使用super調用父類的方法:
+ (void)initialize {
//do initialization thing
[super initialize];
}
+ (void) load {
//do some loading things
[super load];
}
super的方法會成功調用,但是這是多余的,因為runtime對自動對父類的+(void)load方法進行調用,而+(void)initialize則會隨子類自動激發父類的方法(如Apple文檔中所言)不需要顯示調用。另一方面,如果父類中的方法用到的self(像示例中的方法),其指代的依然是類自身,而不是父類。
markdown表格制作方法:
| Tables | +(void)load | +(void)initialize |
| :-----------------: |:-----------------------:| :---------------------: |
| 執行時機 | 在程序運行后立即執行 | 在類的方法第一次被調時執行 |
| 若自身未定義,是否沿用父類的方法?| 否 | 是 |
| 類別中的定義 | 全都執行,但后于類中的方法 | 覆蓋類中的方法,只執行一個 |
Tables | +(void)load | +(void)initialize |
---|---|---|
執行時機 | 在程序運行后立即執行 | 在類的方法第一次被調時執行 |
若自身未定義,是否沿用父類的方法? | 否 | 是 |
類別中的定義 | 全都執行,但后于類中的方法 | 覆蓋類中的方法,只執行一個 |