initialize 与 load的使用注意
2016-12-06 本文已影响60人
_时光念你
load
+load
方法会在程序进入运行期之前调用,而且只会调用一次,但是如果多个分类中都写了该方法,那么所有的都会调用,但是类的load
方法会比分类中的方法先调用,而且分类中的调用次序不确定.
AClass.m
+ (void)load{
[BClass new];
}
BClass.m
+ (void)load
{
[AClass new];
}
- 如果这样写就会有问题,因为调用所有实现
load
方法的类的次序是不确定的,也许当执行AClass的load方法时,B类还没有加载到内存中,所以在load
方法中一定不要调用自己创建的类,但是可以调用Foundation框架
下的类,系统在这之前就已经加载好了Foundation框架
下的所有类. - 在加载之前系统会一口气调用完所有的
load
方法,并阻塞主线程,可能会导致卡顿,所以尽量在load方法中不要做复杂操作,操作越简单越好.如果写的很复杂,就会出现上面的错误,也许嵌套的不像这样简单,那就更难发现.
HGBaseClass.h
#import <Foundation/Foundation.h>
@interface HGBaseClass : NSObject
@end
@interface HGSubClass : HGBaseClass
@end
HGBaseClass.m
#import "HGBaseClass.h"
@implementation HGBaseClass
+ (void)load
{
NSLog(@"%@ initialize",self);
}
@end
@implementation HGSubClass
@end
执行
int main(int argc, const char * argv[]) {
@autoreleasepool {
[HGSubClass new];
}
return 0;
}
2016-12-06 16:20:02.288723 API_Test[99942:642553] HGBaseClass initialize
- 可以看到load只执行了一次,是我们想要的.
- 当前类实现
load
方法不会去调用父类方法,跟initialize
不同,接下来我们看看initialize
会怎么样.
initialize
initialize
方法也只会调用一次,但是并不代表
该方法块只会执行一次(下面示例),这点与load
不同.该方法是懒汉式
,只会等类用到了才会去调用该方法,而且该方法会在线程安全的环境
中运行,无需担心线程安全问题.
接下来我们将HGBaseClass.m文件改成以下:
@implementation HGBaseClass
+ (void)initialize
{
NSLog(@"%@ initialize",self);
}
@end
@implementation HGSubClass
@end
再次运行
int main(int argc, const char * argv[]) {
@autoreleasepool {
[HGSubClass new];
}
return 0;
}
2016-12-06 13:57:23.205882 API_Test[72179:559567] HGBaseClass initialize
2016-12-06 13:57:23.302259 API_Test[72179:559567] HGSubClass initialize
- 可以看出,居然打印了两次,跟
load
不一样啊.这里需要注意的是:虽然initialize
只会执行一次,但是针对的是当前类只执行一次,如果该类有子类,子类也会去执行子类自己的那一次,如果子类没有实现initialize
方法,会去调用父类的方法.很多开发者都知道类的特性,但是未必会注意.所以一般我们会这样写:
+ (void)initialize
{
// 如果是当前类
if (self == [HGBaseClass class]) {
NSLog(@"%@ initialize",self);
}
}
2016-12-06 14:00:58.196463 API_Test[72918:561964] HGBaseClass initialize
也可以这样:
+ (void)initialize
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"%@ initialize",self);
});
}
- 如果子类自己也实现
initialize
方法且没有调用[super initialize],父类的initialize
将会被子类覆盖 - 如果在
initialize
中用了Runtime去swizzle
一些方法,进行方法的实现的调换,就必须要保证该代码只会执行一次.否则会出现BUG