美文网首页#iOS#HeminWon
initialize 与 load的使用注意

initialize 与 load的使用注意

作者: _时光念你 | 来源:发表于2016-12-06 16:11 被阅读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

相关文章

网友评论

    本文标题:initialize 与 load的使用注意

    本文链接:https://www.haomeiwen.com/subject/qcjrmttx.html