一、load方法和initialize方法特點
1)load方法
原型如下
+ (void)load
load函數(shù)調(diào)用特點如下:
- 對于加入運行時系統(tǒng)中的每個類(class)及分類(category)來說,都會調(diào)用此方法,且只會調(diào)用一次。如果分類和其所屬的類都調(diào)用了load方法,則先調(diào)用類里面的,再調(diào)用分類里的。
- load方法并不像普通方法那樣,它并不遵從繼承規(guī)則。即如果某個類本身沒有l(wèi)oad方法,那么不管其超類是否實現(xiàn)load方法,系統(tǒng)都不會調(diào)用。
下面舉個例子說明:
定義一個Pet類繼承自NSObject,再定義一個Dog類繼承自Pet類,在Pet類中重寫load方法,如下:
@interface Pet : NSObject
@end
@implementation Pet
+ (void)load {
NSLog(@"%@ %s", self, __FUNCTION__);
}
@end
@interface Dog : Pet
@end
@implementation Dog
@end
運行,輸出結(jié)果如下:
Pet +[Pet load]
可以發(fā)現(xiàn),子類并沒有調(diào)用父類的方法。(可以與initialize方法做比較)
2)initialize方法
其原型如下:
+ (void)initialize
initialize函數(shù)的特點是:
- 對每個類來說,該方法會在程序首次使用該類前調(diào)用,且只調(diào)用一次。它是由運行時系統(tǒng)來調(diào)用的,絕不應(yīng)該通過代碼直接調(diào)用。
- initialize方法與其他消息一樣,如果某個類未實現(xiàn)它,而其超類實現(xiàn)了,就會運行超類的實現(xiàn)代碼。
同樣以一個例子說明:
在Pet類中重寫initialize方法,而在Dog類中并不重寫initialize方法,在main函數(shù)中初始化一個Dog類實例,如下:
@interface Pet : NSObject
@end
@implementation Pet
+ (void)initialize {
NSLog(@"%@ %s", self, __FUNCTION__);
}
@end
@interface Dog : Pet
@end
@implementation Dog
@end
int main(int argc, char * argv[]) {
@autoreleasepool {
Dog *dog = [[Dog alloc] init];
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
輸出的結(jié)果如下:
Pet +[Pet initialize]
Dog +[Pet initialize]
可以發(fā)現(xiàn),即使Dog類沒有實現(xiàn)initialize方法,它也會收到這條消息。這樣會很奇怪,當(dāng)初始化Pet類的時候,Pet類中的定義的initialize方法會運行一遍,而當(dāng)初始化子類Dog類時,由于該子類并沒有重寫initialize方法,因此還要把父類的initialize方法再運行一次。所以,通常initialize方法的實現(xiàn)如下:
+ (void)initialize {
if(self == [Pet class]){
NSLog(@"%@ %s", self, __FUNCTION__);
}
}
二、load和initialize的總結(jié)
1)加載不同
- initialize是”懶加載調(diào)用”,即只有當(dāng)用到了相關(guān)的類時,才會調(diào)用。如果某個類一直都沒有使用,則其initialize方法就一直不會運行。這也就是說,應(yīng)用程序無須把每個類的initialize都執(zhí)行一遍。
- 這就與load不同,對于load來說,應(yīng)用程序必須阻塞并等待所有類的load都執(zhí)行完,才能繼續(xù)。
2)線程安全
- initialize在運行期系統(tǒng)執(zhí)行該方法時,是處于正常狀態(tài),因此從運行期系統(tǒng)完整度上來講,此時可以安全使用并調(diào)用任意類中的任意方法。而且,運行期系統(tǒng)也能保證initialize方法一定會在“線程安全的環(huán)境(thread-safe environment)”中執(zhí)行,這就是說,只有執(zhí)行initialize的那個線程可以操作類或者類實例。其他線程都要先阻塞,等著initialize執(zhí)行完。
- load方法的問題在于,執(zhí)行該方法時,運行期系統(tǒng)處于“脆弱狀態(tài)(fragile state)”。在執(zhí)行子類的load方法之前,必定會執(zhí)行所有超類的load方法。
三、load和initialize方法的使用
load方法的應(yīng)用場景可參考:
需要注意的是,load方法代碼務(wù)必精簡,也就是要盡量減少其所執(zhí)行的操作,因為整個應(yīng)用程序在執(zhí)行l(wèi)oad方法時都會阻塞。如果load方法中包含復(fù)雜的代碼,那么應(yīng)用程序在執(zhí)行期間就會變的無響應(yīng)。實際上,凡是想通過load在類加載之前執(zhí)行某些任務(wù)的,基本都做得不太對。load方法真正用途在與調(diào)試程序,比如可以在分類中編寫此方法,用來判斷該分類是否已經(jīng)正確載入系統(tǒng)中。load方法以前用的較多,但現(xiàn)在建議盡量不要使用。
initialize方法應(yīng)該只用來設(shè)置內(nèi)部數(shù)據(jù)。不應(yīng)該在其中調(diào)用其他方法,即便是本類自己的方法,也最好別調(diào)用。因為稍后可能還要給那些方法添加更多的功能。如果某個全局狀態(tài)無法在編譯期初始化,則可以放在initialize里面來做。下列代碼演示了這種用法:
//EOCClass.h
@interface EOCClass : NSObject
@end
// EOCClass.m
#import "EOCClass.h"
static const int kInterval = 10;
static NSMutableArray *kSomeObjects;
@implementation EOCClass
+ (void)initialize {
if (self == [EOCClass class]) {
kSomeObjects = [NSMutableArray new];
}
}
@end
整數(shù)可以在編譯時定義,可變數(shù)組不行,因為其是個Objective-C對象,所以創(chuàng)建實例之前必須先激活運行時系統(tǒng)。注意,某些Objective-C對象也可以在編譯時創(chuàng)建,例如NSString實例。