每日一问07——+load&+initialize

简介:+ initialize 和 + load 是 NSObject 类的两个类方法,它们会在运行时自动调用。

先看个例子

@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

运行程序后,打印结果为SubClass load。说明程序启动会自动调用load,而不会调用+ (void)initialize

再改进一下例子

int main(int argc, char * argv[]) {
    @autoreleasepool {
        BaseClass *class = [[BaseClass alloc] init];
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

在main函数里面添加BaseClass初始化操作。打印结果为SubClass load ,+[BaseClass initialize] 。先猜测一下,+load是会100%调用的,而+ initialize只会在这个类被使用的时候才被调用。

+ initialize

先看看 NSObject Class Reference 中关于 +initialize
的说明:

1.+ (void)initialize 消息是在该类接收到其第一个消息之前调用。关于这里的第一个消息需要特别说明一下,对于 NSObjectruntime 机制而言,其在调用 NSObject+ (void)load 消息不被视为第一个消息,但是,如果像普通函数调用一样直接调用 NSObject+ (void)load 消息,则会引起 + (void)initialize 的调用。反之,如果没有向 NSObject 发送第一个消息,+ (void)initialize 则不会被自动调用。

2.在应用程序的生命周期中,runtime 只会向每个类发送一次 + (void)initialize 消息,如果该类是子类,且该子类中没有实现 + (void)initialize 消息,或者子类显示调用父类实现 [super initialize], 那么则会调用其父类的实现。也就是说,父类的 + (void)initialize 可能会被调用多次。

3.如果类包含分类,且分类重写了initialize方法,那么则会调用分类的initialize 实现,而原类的该方法实现不会被调用,这个机制同 NSObject 的其他方法(除 + (void)load 方法) 一样,即如果原类同该类的分类包含有相同的方法实现,那么原类的该方法被隐藏而无法被调用。

4.父类的 initialize 方法先于子类的 initialize 方法调用。

  • 先改进一下例子
@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]));
    }
}

打印结果为+[BaseClass initialize],+[SubClass initialize]。验证结果先调用了BaseClassinitialize后调用了SubClassinitialize

  • 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

打印结果为 +[BaseClass initialize],+[SubClass initialize],viewDidLoad,viewWillAppear。说明runtime的确只会向每个类发送一次+ (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

打印结果是+[BaseClass initialize],+[SubClass(test) initialize],可以看出当分类实现+initialize后会覆盖掉原来类中的+initialize方法

+load

先看看 NSObject Class Reference 中关于 + (void)load
的说明:

1.+ (void)load 会在类或者类的分类添加到 Objective-c runtime 时调用,该调用发生在 application:willFinishLaunchingWithOptions: 调用之前调用。
2.父类的 +load 方法先于子类的 +load 方法调用,类本身的 +load 方法先于分类的 +load 方法调用。

为刚才的类添加+load方法实现

@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;
}

打印顺序为

+[BaseClass initialize]
BaseClass +[BaseClass load]
+[SubClass(test) initialize]
SubClass +[SubClass load]
SubClass +[SubClass(test) load]
didFinishLaunchingWithOptions

从这个例子可以出,load方法不会被分类的+load覆盖,父类的load方法先调用,load方法在didFinishLaunchingWithOptions执行前调用。

回到最开始的例子,

@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]

验证了当子类没有实现initialize时,父类的initialize调用了2次且 +initialize 的调用在 +load 调用之前,这是因为我们在 +load 实现中包含 [self class] 的调用。

到这里,+ initialize+load的调用顺序与特性就验证的差不多了。那么知道了这些在开发的时候具体有什么用处呢?

实际案例

+load案例

load使用示例1, 见 @sunnyxx 大神的博客 Notification Once, 用于给 AppDelegate 瘦身。
load使用示例2, 见博客 Method Swizzling 和 AOP 实践, 在UIViewController的 +load 时期执行IMP替换,实现AOP。

+ initialize案例

使用initialize与static实现单例模式:

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

打印结果

single1 0x60000000fb10
single2 0x60000000fb10
single3 0x60000000fb10

都访问的同一块内存,只初始化了一次。
同理我们也可以借助initialize初始化一些全局变量,静态变量。

最后再补充一下:load和initialize方法内部使用了锁,因此它们是线程安全的。实现时要尽可能保持简单,避免阻塞线程,不要再使用锁。

相关文章

Objective-C类初始化:load与initialize
NSObject +load and +initialize - What do they do?
iOS初探+load和+initialize

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容