1. 程式人生 > >寫高質量OC程式碼52建議總結:51.load和initialize

寫高質量OC程式碼52建議總結:51.load和initialize

有時候,類必須先執行某些初始化操作才能正常執行,先說下load方法:
 +(void)load
 加入執行期系統中的每個類及分類必定會呼叫此方法,只調用一次(在程式啟動的時候)。如果類和所屬的分類都定義了load方法,先呼叫類中的,再呼叫分類中的。

 load方法的問題在於,執行的時候系統處在“脆弱時期”,執行子類load之前必定會執行超類load,程式碼如果依賴了其他類,相關類中的load方法也會先執行,但是無法判斷各個類的載入順序。所以在load中使用其他類的方法是不安全的。例如:

+(void)load{
    NSLog(@"Loading EOCClassB");
    EOCClassA *object = [EOCClassA new];
}
 nslog沒問題,Foundation框架一定在執行load前就載入系統了。但是在EOCClassB中執行EOCClassA中的方法就不安全了,因為此時不確定EOCClassA已經載入好了。
 load不遵循繼承規則,如果類本身沒實現load方法,不管父類是否實現都不會被呼叫。如果都實現了,類比分類先呼叫load。
 load中的程式碼必須精簡。甚至能不寫東西就別寫東西。。。如果load中包含繁瑣的程式碼,程式在執行期間就會變得無響應。load主要用於除錯程式,正常編碼一般不需要。

 要執行與類相關的初始化操作還可以覆寫下面的方法:
 +(void)initialize;
 
 程式會在首次使用該類的時候呼叫,只調用一次,由系統呼叫不能手動呼叫。它是惰性呼叫的,如果用到了該類才會呼叫該方法,沒用到就不呼叫。這點和load不同(load必須把所有類都執行完程式才能繼續)。系統在呼叫該方法時是安全的,可以安全的呼叫並使用任意類中的任意方法。系統也會保證initialize的執行緒安全,只有執行initialize的執行緒可以操作類。其他執行緒先阻塞等待initialize執行完。還有一點,initialize遵循繼承協議,父類實現了該方法子類沒有實現。執行時,系統都會呼叫該方法。

@interface EOCBaseClass : NSObject

@end

@implementation EOCBaseClass

+(void)initialize{
    NSLog(@"%@ initialize", self);
}

@end



@interface EOCSubClass : EOCBaseClass
@end

@implementation EOCSubClass
@end
 即使EOCSubClass沒有實現initialize,他也會受到訊息。
 EOCBaseClass initialize,EOCSubClass initialize
 所以,通常這樣實現:
 +(void)initialize{
     if(self == [EOCBaseClass class]) {
         NSLog(@"%@ initialize", self);
    }
 }
 控制檯只會輸出一條資訊了,EOCBaseClass initialize。
 
 load和initialize,在裡面設定一些狀態使本類可以正常運作就可以了,不要加入太複雜的程式碼。我們無法控制類初始化的時間,不可控。假如一個類的初始化方法很複雜,其中可能直接或間接用到其他的類,本類的初始化方法此時還沒實行,如果其他類初始化的時候需要本類的一些資料,就會產生錯誤。
static id EOCClassAInternalData;
@interface EOCClassA : NSObject
@end

static id EOCClassBInternalData;
@interface EOCClassB : NSObject
@end

@implementation EOCClassA

+(void)initialize{
    if (self == [EOCClassA class]) {
        //[EOCClassB doSomething];
        EOCClassAInternalData = [self setupInternalData];
    }
}

@end

@implementation EOCClassB

+(void)initialize{
    if (self == [EOCClassB class]) {
        //[EOCClassA doSomething];
        EOCClassBInternalData = [self setupInternalData];
    }
}

@end
 如果A先初始化,隨後B初始化,會在自己的初始化方法中呼叫A的doSomething方法,此時A的內部資料還沒準備好。
 如果某個全域性狀態無法在編譯器初始化可以放到initialize中。
@interface EOCClass : NSObject

@end

static const int kInterval = 10;
static NSMutableArray *kSomeObjects;

@implementation EOCClass

+(void)initialize{
    if (self == [EOCClass class]) {
        kSomeObjects = [NSMutableArray new];
    }
}

@end
 整數可以在編譯器編譯,可變陣列不行,它是個OC物件,建立例項的時候必須先啟用系統。
 
 總結:
 1.load方法不參與覆寫機制。
 2.initialize參與覆寫機制,通常在其中判斷當前要初始化哪個類。
 3.load和initialize中的程式碼都應該精簡,保證程式的響應能力。
 4.無法在編譯器初始化的全域性常量,可以在initialize中初始化。