簡介:+ initialize 和 + load 是 NSObject 類的兩個類方法啤覆,它們會在運(yùn)行時自動調(diào)用。
先看個例子
@interface BaseClass : NSObject
@end
@implementation BaseClass
+ (void)initialize {
NSLog(@"%@ , %s", [self class], __FUNCTION__);
}
@end
@interface SubClass : BaseClass
@end
@implementation SubClass
+ (void)load {
NSLog(@"SubClass load");
}
@end
運(yùn)行程序后宅楞,打印結(jié)果為SubClass load针姿。說明程序啟動會自動調(diào)用load,而不會調(diào)用+ (void)initialize
再改進(jìn)一下例子
int main(int argc, char * argv[]) {
@autoreleasepool {
BaseClass *class = [[BaseClass alloc] init];
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
在main函數(shù)里面添加BaseClass初始化操作厌衙。打印結(jié)果為SubClass load ,+[BaseClass initialize] 距淫。先猜測一下,+load是會100%調(diào)用的婶希,而+ initialize只會在這個類被使用的時候才被調(diào)用榕暇。
+ initialize
先看看 NSObject Class Reference 中關(guān)于 +initialize
的說明:
1.+ (void)initialize
消息是在該類接收到其第一個消息之前調(diào)用。關(guān)于這里的第一個消息需要特別說明一下喻杈,對于 NSObject
的 runtime
機(jī)制而言拐揭,其在調(diào)用 NSObject
的 + (void)load
消息不被視為第一個消息,但是奕塑,如果像普通函數(shù)調(diào)用一樣直接調(diào)用 NSObject
的 + (void)load
消息堂污,則會引起 + (void)initialize
的調(diào)用。反之龄砰,如果沒有向 NSObject
發(fā)送第一個消息盟猖,+ (void)initialize
則不會被自動調(diào)用。
2.在應(yīng)用程序的生命周期中换棚,runtime
只會向每個類發(fā)送一次 + (void)initialize
消息式镐,如果該類是子類,且該子類中沒有實(shí)現(xiàn) + (void)initialize
消息固蚤,或者子類顯示調(diào)用父類實(shí)現(xiàn) [super initialize]
, 那么則會調(diào)用其父類的實(shí)現(xiàn)娘汞。也就是說,父類的 + (void)initialize
可能會被調(diào)用多次夕玩。
3.如果類包含分類你弦,且分類重寫了initialize
方法惊豺,那么則會調(diào)用分類的initialize
實(shí)現(xiàn),而原類的該方法實(shí)現(xiàn)不會被調(diào)用禽作,這個機(jī)制同 NSObject
的其他方法(除 + (void)load
方法) 一樣尸昧,即如果原類同該類的分類包含有相同的方法實(shí)現(xiàn),那么原類的該方法被隱藏而無法被調(diào)用旷偿。
4.父類的 initialize
方法先于子類的 initialize
方法調(diào)用烹俗。
- 先改進(jìn)一下例子
@implementation BaseClass
+ (void)initialize {
// NSLog(@"%@ , %s", [self class], __FUNCTION__);
NSLog(@"%s", __FUNCTION__);
}
@end
@implementation SubClass
+ (void)initialize {
NSLog(@"%s", __FUNCTION__);
}
@end
int main(int argc, char * argv[]) {
@autoreleasepool {
SubClass *class = [[SubClass alloc] init];
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
打印結(jié)果為+[BaseClass initialize],+[SubClass initialize]。驗(yàn)證結(jié)果先調(diào)用了BaseClass
的initialize
后調(diào)用了SubClass
的initialize
- 再
ViewController
里測試
@implementation ViewController
- (void)viewWillAppear:(BOOL)animated {
SubClass *class = [[SubClass alloc] init];
NSLog(@"viewWillAppear");
}
- (void)viewDidLoad {
[super viewDidLoad];
SubClass *class = [[SubClass alloc] init];
NSLog(@"viewDidLoad");
}
@end
打印結(jié)果為 +[BaseClass initialize],+[SubClass initialize],viewDidLoad,viewWillAppear萍程。說明runtime的確只會向每個類發(fā)送一次+ (void)initialize 消息幢妄。
- 給SubClass添加一個分類試試
@implementation SubClass (test)
+ (void)initialize {
NSLog(@"%s", __FUNCTION__);
}
@end
@implementation ViewController
- (void)viewWillAppear:(BOOL)animated {
SubClass *class = [[SubClass alloc] init];
NSLog(@"viewWillAppear");
}
- (void)viewDidLoad {
[super viewDidLoad];
SubClass *class = [[SubClass alloc] init];
NSLog(@"viewDidLoad");
}
@end
打印結(jié)果是+[BaseClass initialize],+[SubClass(test) initialize]茫负,可以看出當(dāng)分類實(shí)現(xiàn)+initialize
后會覆蓋掉原來類中的+initialize
方法
+load
先看看 NSObject Class Reference 中關(guān)于 + (void)load
的說明:
1.+ (void)load 會在類或者類的分類添加到 Objective-c runtime
時調(diào)用磁浇,該調(diào)用發(fā)生在 application:willFinishLaunchingWithOptions:
調(diào)用之前調(diào)用。
2.父類的 +load
方法先于子類的 +load
方法調(diào)用朽褪,類本身的 +load
方法先于分類的 +load
方法調(diào)用置吓。
為剛才的類添加+load方法實(shí)現(xiàn)
@implementation SubClass
+ (void)initialize {
NSLog(@"%s", __FUNCTION__);
}
+ (void)load {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
@end
@implementation SubClass (test)
+ (void)initialize {
NSLog(@"%s", __FUNCTION__);
}
+ (void)load {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
@end
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NSLog(@"didFinishLaunchingWithOptions");
return YES;
}
打印順序?yàn)?/p>
+[BaseClass initialize]
BaseClass +[BaseClass load]
+[SubClass(test) initialize]
SubClass +[SubClass load]
SubClass +[SubClass(test) load]
didFinishLaunchingWithOptions
從這個例子可以出,load方法不會被分類的+load覆蓋缔赠,父類的load方法先調(diào)用衍锚,load方法在didFinishLaunchingWithOptions執(zhí)行前調(diào)用。
回到最開始的例子嗤堰,
@implementation BaseClass
+ (void)initialize {
// NSLog(@"%@ , %s", [self class], __FUNCTION__);
NSLog(@"%s", __FUNCTION__);
}
@end
@implementation SubClass
+ (void)load {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
@end
SubClass *class = [[SubClass alloc] init];
打印順序
+[BaseClass initialize]
+[BaseClass initialize]
SubClass +[SubClass load]
驗(yàn)證了當(dāng)子類沒有實(shí)現(xiàn)initialize時戴质,父類的initialize調(diào)用了2次且 +initialize
的調(diào)用在 +load
調(diào)用之前,這是因?yàn)槲覀冊?+load
實(shí)現(xiàn)中包含 [self class]
的調(diào)用踢匣。
到這里告匠,+ initialize
和+load
的調(diào)用順序與特性就驗(yàn)證的差不多了。那么知道了這些在開發(fā)的時候具體有什么用處呢离唬?
實(shí)際案例
+load案例
load使用示例1, 見 @sunnyxx 大神的博客 Notification Once, 用于給 AppDelegate 瘦身后专。
load使用示例2, 見博客 Method Swizzling 和 AOP 實(shí)踐, 在UIViewController的 +load 時期執(zhí)行IMP替換,實(shí)現(xiàn)AOP输莺。
+ initialize案例
使用initialize與static實(shí)現(xiàn)單例模式:
static SingleModel *initTest = nil;
@implementation SingleModel
+ (void)initialize
{
NSLog(@"InitTest : initialize className : %@",[self class]);
if (initTest == nil) {
initTest = [[SingleModel alloc] init];
}
}
+ (SingleModel *)defaultManager
{
return initTest;
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
SingleModel *single1 = [SingleModel defaultManager];
SingleModel *single2 = [SingleModel defaultManager];
SingleModel *single3 = [SingleModel defaultManager];
NSLog(@"single1 %p",single1);
NSLog(@"single2 %p",single2);
NSLog(@"single3 %p",single3);
}
@end
打印結(jié)果
single1 0x60000000fb10
single2 0x60000000fb10
single3 0x60000000fb10
都訪問的同一塊內(nèi)存戚哎,只初始化了一次。
同理我們也可以借助initialize初始化一些全局變量嫂用,靜態(tài)變量型凳。
最后再補(bǔ)充一下:load和initialize方法內(nèi)部使用了鎖,因此它們是線程安全的嘱函。實(shí)現(xiàn)時要盡可能保持簡單甘畅,避免阻塞線程,不要再使用鎖。
相關(guān)文章
Objective-C類初始化:load與initialize
NSObject +load and +initialize - What do they do?
iOS初探+load和+initialize