+ (void)load和 + (void)initialize
+ initialize 和 + load 是 NSObject 類的兩個(gè)類方法,它們會在運(yùn)行時(shí)自動(dòng)調(diào)用,我們可以利用其特性做一些初始化操作。
initialize和load的區(qū)別在于:load是只要類所在文件被引用就會被調(diào)用,而initialize是在類或者其子類的第一個(gè)方法被調(diào)用前調(diào)用。所以如果類沒有被引用進(jìn)項(xiàng)目,就不會有l(wèi)oad調(diào)用;但即使類文件被引用進(jìn)來,但是沒有使用,那么initialize也不會被調(diào)用。
initialize初探
+ (void)initialize 消息是在該類接收到其第一個(gè)消息之前調(diào)用。
關(guān)于這里的第一個(gè)消息需要特別說明一下,對于 NSObject 的 runtime 機(jī)制而言,其在調(diào)用 NSObject 的 + (void)load 消息不被視為第一個(gè)消息,但是,如果像普通函數(shù)調(diào)用一樣直接調(diào)用 NSObject 的 + (void)load 消息,則會引起 + (void)initialize 的調(diào)用。反之,如果沒有向 NSObject 發(fā)送第一個(gè)消息,+ (void)initialize 則不會被自動(dòng)調(diào)用。
在應(yīng)用程序的生命周期中,runtime 只會向每個(gè)類發(fā)送一次 + (void)initialize 消息.
如果該類是子類,且該子類中沒有實(shí)現(xiàn) + (void)initialize 消息,或者子類顯示調(diào)用父類實(shí)現(xiàn) [super initialize], 那么則會調(diào)用其父類的實(shí)現(xiàn)。也就是說,父類的 + (void)initialize 可能會被調(diào)用多次。
如果類包含分類(擴(kuò)展 catagory),且分類重寫了initialize方法,那么則會調(diào)用分類的 initialize 實(shí)現(xiàn),而原類的該方法實(shí)現(xiàn)不會被調(diào)用。
這個(gè)機(jī)制同 NSObject 的其他方法(除 + (void)load 方法) 一樣,即如果原類同該類的分類包含有相同的方法實(shí)現(xiàn),那么原類的該方法被隱藏而無法被調(diào)用。
父類的 initialize 方法先于子類的 initialize 方法調(diào)用。
實(shí)例:
@interface People : NSObject
@end
@implementation People
+ (void)initialize {
NSLog(@"%s", __FUNCTION__);
}
@end
@interface Student : People
@end
@implementation Student
+ (void)initialize {
NSLog(@"%s", __FUNCTION__);
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Student *student = [[Student alloc] init];
}
@end
輸出:
+[People initialize]
+[Student initialize]
若再次調(diào)用初始化實(shí)例,initialize 不再調(diào)用。
擴(kuò)展情況下:
@interface Student (Score)
@end
@implementation Student (Score)
+ (void)initialize {
NSLog(@"%s", __FUNCTION__);
}
@end
輸出:
+[People initialize]
+[Student(Score) initialize]
load初探
+ (void)load 會在類或者類的分類添加到 Objective-c runtime 時(shí)調(diào)用,該調(diào)用發(fā)生在 application:willFinishLaunchingWithOptions: 調(diào)用之前調(diào)用。
父類的 +load 方法先于子類的 +load 方法調(diào)用,分類的 +load 方法先于類本身的 +load 方法調(diào)用。
Runtime調(diào)用+(void)load時(shí)沒有autorelease pool:
原因是runtime調(diào)用+(void)load的時(shí)候,程序還沒有建立其autorelease pool,所以那些會需要使用到autorelease pool的代碼,都會出現(xiàn)異常。這一點(diǎn)是非常需要注意的,也就是說放在+(void)load中的對象都應(yīng)該是alloc出來并且不能使用autorelease來釋放。
不需要顯示使用super調(diào)用父類中的方法
super的方法會成功調(diào)用,但是這是多余的,因?yàn)閞untime對自動(dòng)對父類的+(void)load方法進(jìn)行調(diào)用,而+(void)initialize則會隨子類自動(dòng)激發(fā)父類的方法(如Apple文檔中所言)不需要顯示調(diào)用。另一方面,如果父類中的
實(shí)例:
//父類
@interface People : NSObject
@end
@implementation People
+ (void)initialize {
NSLog(@"%@ , %s", [self class], __FUNCTION__);
}
+ (void)load {
NSLog(@"%@, %s", [self class], __FUNCTION__);
}
@end
//子類
@interface Student : People
@end
@implementation Student
+ (void)initialize {
NSLog(@"%@, %s", [self class], __FUNCTION__);
}
+ (void)load {
NSLog(@"%@, %s", [self class], __FUNCTION__);
}
@end
//子類分類
@interface Student (Score)
@end
@implementation Student (Score)
+ (void)initialize {
NSLog(@"%@, %s", [self class], __FUNCTION__);
}
+ (void)load {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
@end
輸出:
People , +[People initialize]
People, +[People load]
Student, +[Student(Score) initialize]
Student, +[Student load]
Student +[Student(Score) load]
實(shí)例二:
//父類
@interface People : NSObject
@end
@implementation People
+ (void)initialize {
NSLog(@"%@ , %s", [self class], __FUNCTION__);
}
@end
//子類
@interface Student : People
@end
@implementation Student
+ (void)load {
NSLog(@"%@, %s", [self class], __FUNCTION__);
}
@end
輸出:
People , +[People initialize]
Student , +[People initialize]
Student, +[Student load]
當(dāng)子類沒有實(shí)現(xiàn) +initialize 而父類有其實(shí)現(xiàn)時(shí),父類的實(shí)現(xiàn)調(diào)用了兩次,且 +initialize 的調(diào)用在 +load 調(diào)用之前,這是因?yàn)槲覀冊?+load 實(shí)現(xiàn)中包含 [self class] 的調(diào)用。
總結(jié):
+(void)load
執(zhí)行時(shí)機(jī) 在程序運(yùn)行后立即執(zhí)行
若自身未定義,是否沿用父類的方法? 否
類別中的定義 全都執(zhí)行,但后于類中的方法
+(void)initialize
執(zhí)行時(shí)機(jī) 在類的方法第一次被調(diào)時(shí)執(zhí)行
若自身未定義,是否沿用父類的方法? 是
類別中的定義 覆蓋類中的方法,只執(zhí)行一個(gè)