load和initialize的區別

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

它們的相同點在于:

方法只會被調用一次(其實這是相對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
執行時機 在程序運行后立即執行 在類的方法第一次被調時執行
若自身未定義,是否沿用父類的方法?
類別中的定義 全都執行,但后于類中的方法 覆蓋類中的方法,只執行一個
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容