你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS load和initialize的區別

iOS load和initialize的區別

編輯:IOS開發綜合

過去兩個星期裡,為了完成一個工作,接觸到了NSObject中非常特別的兩個類方法(Class Method)。它們的特別之處,在於iOS會在運行期提前並且自動調用這兩個方法,而且很多對於類方法的規則(比如繼承,類別(Category))都有不同的處理。

而因為這兩個方法是在程序運行一開始就被調用的方法,我們可以利用他們在類被使用前,做一些預處理工作。比如我碰到的就是讓類自動將自身類名保存到一個NSDictionary中。

 

先來看看NSObject Class Reference裡對這兩個方法說明:

+(void)initialize

The runtime sendsinitializeto each class in a program exactly one time just before the class, or any class that inherits from it, is sent its first message from within the program. (Thus the method may never be invoked if the class is not used.) The runtime sends the initialize message to classes in a thread-safe manner.Superclasses receive this message before their subclasses.

+(void)load

Theloadmessage is sent to classes and categories that are both dynamically loaded and statically linked, but only if the newly loaded class or category implements a method that can respond.
The order of initialization is as follows:

  1. All initializers in any framework you link to.
  2. All+loadmethods in your image.
  3. All C++ static initializers and C/C++__attribute__(constructor)functions in your image.
  4. All initializers in frameworks that link to you.

    In addition:

    • A class’s+loadmethod is called after all of its superclasses’+loadmethods.
    • A category+loadmethod is called after the class’s own+loadmethod.

      In a custom implementation ofloadyou can therefore safely message other unrelated classes from the same image, but anyloadmethods implemented by those classes may not have run yet.

Apple的文檔很清楚地說明了initialize和load的區別在於:load是只要類所在文件被引用就會被調用,而initialize是在類或者其子類的第一個方法被調用前調用。所以如果類沒有被引用進項目,就不會有load調用;但即使類文件被引用進來,但是沒有使用,那麼initialize也不會被調用。

它們的相同點在於:方法只會被調用一次。(其實這是相對runtime來說的,後邊會做進一步解釋)。

文檔也明確闡述了方法調用的順序:父類(Superclass)的方法優先於子類(Subclass)的方法,類中的方法優先於類別(Category)中的方法。

不過還有很多地方是文章中沒有解釋詳細的。所以再來看一些示例代碼來明確其中應該注意的細節。

+(void)load與+(void)initialize初探

 1 +(void)load會引發+(void)initialize
 2 /******* Interface *******/
 3 @interface SuperClass : NSObject
 4 @end
 5 
 6 @interface ChildClass : SuperClass
 7 @end
 8 
 9 @interface Insideinitialize : NSObject
10 - (void)objectMethod;
11 @end
12 
13 /******* Implementation *******/
14 @implementation SuperClass
15 
16 + (void) initialize {
17     NSLog(@"%@ %s", [self class], __FUNCTION__);
18 }
19 
20 + (void) load {
21     NSLog(@"%@ %s", [self class], __FUNCTION__);
22 }
23 
24 @end
25 
26 @implementation ChildClass
27 
28 + (void) initialize {
29     NSLog(@"%@ %s", [self class], __FUNCTION__);
30     Insideinitialize * obj = [[Insideinitialize alloc] init];
31     [obj objectMethod];
32     [obj release];
33 }
34 
35 @end
36 
37 @implementation Insideinitialize
38 
39 - (void)objectMethod {
40     NSLog(@"%@ %s", [self class], __FUNCTION__);
41 }
42 
43 + (void) initialize {
44     NSLog(@"%@ %s", [self class], __FUNCTION__);
45 }
46 
47 + (void) load {
48     NSLog(@"%s", __FUNCTION__);
49 }
50 
51 @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:

[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

 1 /******* Interface *******/
 2 @interface SuperClass : NSObject
 3 @end
 4 
 5 @interface ChildClass : SuperClass
 6 @end
 7 
 8 @interface Insideinitialize : NSObject
 9 - (void)objectMethod;
10 @end
11 
12 /******* Implementation *******/
13 @implementation SuperClass
14 
15 + (void) initialize {
16     NSLog(@"%@ %s", [self class], __FUNCTION__);
17 }
18 
19 @end
20 
21 @implementation ChildClass
22 
23 + (void) load {
24     NSLog(@"%@ %s", [self class], __FUNCTION__);
25 }
26 
27 @end

依然還是簡單的引入這些類,並不去使用它們。運行之後,我們會得到這樣的結果:

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的區別。

 1 /******* Interface *******/
 2 @interface MainClass : NSObject
 3 @end
 4 
 5 /******* Category Implementation *******/
 6 @implementation MainClass(Category)
 7 
 8 + (void) load {
 9     NSLog(@"%@ %s", [self class], __FUNCTION__);
10 }
11 
12 + (void) initialize {
13     NSLog(@"%@ %s", [self class], __FUNCTION__);
14 }
15 
16 @end
17 
18 @implementation MainClass(OtherCategory)
19 
20 + (void) load {
21     NSLog(@"%@ %s", [self class], __FUNCTION__);
22 }
23 
24 + (void) initialize {
25     NSLog(@"%@ %s", [self class], __FUNCTION__);
26 }
27 
28 @end
29 
30 /******* Implementation *******/
31 @implementation MainClass
32 
33 + (void) load {
34     NSLog(@"%@ %s", [self class], __FUNCTION__);
35 }
36 
37 + (void) initialize {
38     NSLog(@"%@ %s", [self class], __FUNCTION__);
39 }
40 
41 @end

簡單import,運行,我們看到的結果是:

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

最後再來看一個示例

 1 @interface MainClass : NSObject
 2 @end
 3 
 4 @implementation MainClass
 5 
 6 + (void) load {
 7     NSArray *array = [NSArray array];
 8     NSLog(@"%@ %s", array, __FUNCTION__);
 9 }
10 
11 @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#那樣會自動調用父類默認構造函數。因此我們總是需要將這兩個函數寫成這樣:

 1 - (id)init {
 2     if ((self = [super init])) {
 3         //do initialization
 4     }
 5     
 6     return self;
 7 }
 8 
 9 - (void)dealloc {
10     //do release
11     
12     [super dealloc];
13 }

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

1 + (void)initialize {
2     //do initialization thing
3     [super initialize];
4 }
5 
6 + (void) load {
7     //do some loading things
8     [super load];
9 }

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

總結:

  +(void)load +(void)initialize 執行時機 在程序運行後立即執行 在類的方法第一次被調時執行 若自身未定義,是否沿用父類的方法? 否 是 類別中的定義 全都執行,但後於類中的方法 覆蓋類中的方法,只執行一個
  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved