NSObject 的 initialize 和 load 方法

作为 NSObject 类中的 2 个方法 initialize 和 load 一直被我们所熟知,但是又没有具体去深入的了解,今天结合 Apple 官方文档,我们来深入了解一下 initialize 和 load 方法 。

initialize

看文档可以得知 initialize 方法的作用是在 class 收到第一个消息之前初始化 class。

Initializes the class before it receives its first message.

+ (void)initialize;
  1. runtime 会在程序的 class 收到第一个消息之前给每一个 class 发送 initialize 消息,让 class 进行初始化。
  2. Superclasses 会在 Subclasses 之前收到 initialize 消息。
  3. initialize 方法是线程安全的,在 initialize 方法运行期间, class会被锁定,其他的线程无法向该 class 发送消息,所以我们尽量避免在 initialize 方法里面做复杂的实现。

接下来我们用代码来探究 initialize 这个方法,我们有 3 个 class ,分别是 Man,Woman,Person。 其中 Man 和 Woman 都继承自 Person 。


#import <Foundation/Foundation.h>

@interface Person : NSObject

@end


#import "Person.h"

@implementation Person
+(void)initialize{
    NSLog(@"call person initialize");
}

@end
#import "Person.h"

@interface Man : Person

@end

#import "Man.h"

@implementation Man

@end


#import "Person.h"

@interface Woman : Person

@end

#import "Woman.h"

@implementation Woman

@end

在 main.m 方法代码如下:

#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
    }
    return 0;
}

我们运行程序得到如下输出

 call person initialize

在 Person 的 initialize 方法设置断点,查看堆栈调用,Person 调用的第一个方法确实是 initialize ,该方法在 Person 收到第一个消息之前初始化 Person。

image.png

接下来修改 main.m 的代码,同时生成 Person ,Man,Woman 的对象实例。

#import <Foundation/Foundation.h>
#import "Person.h"
#import "Man.h"
#import "Woman.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
        Man *m = [[Man alloc] init];
        Woman *w = [[Woman alloc] init];
    }
    return 0;
}

运行程序,查看控制台输出

 call person initialize
 call person initialize
 call person initialize

我们可以看出如果子类没有实现 initialize 方法,runtime 会调用父类的 initialize 实现,那么我们就不用在子类中调用 [super initialize]
,同时也意味着父类的 initialize 会被多次调用,那么我们可以采用如下的方式来避免这个问题。

+ (void)initialize {
  if (self == [ClassName self]) {
    // ... do the initialization ...
  }
}

那如果 Person子类 Man 和 Woman 都实现 initialize 方法呢?

@implementation Man
+(void)initialize{
    NSLog(@"call man initialize");
}

+(void)initialize{
    NSLog(@"call woman initialize");
}
@end


运行程序,查看控制台输出,可以看到 class 都是调用各自的 initialize 方法实现。

call person initialize
call man initialize
call woman initialize

每个 class 有且仅有一次 initialize 方法调用,如果想要实现 class 和 category 的分别独立初始化,我们应该使用 load 方法。

load

看文档可以得知 class 或者 category 被添加到 runtime 的时候,load 方法就会被调用。

Invoked whenever a class or category is added to the Objective-C runtime; 
implement this method to perform class-specific behavior upon loading.

+ (void)load;
  1. 和 initialize 方法类似,class 的 load 方法会在 superlcasses 的 load 方法调用之后被调用。
  2. category 的 load 方法会在 class 的 load 方法调用之后被调用。

接下来我们用代码来探究 load 这个方法,还是用之前的例子,我们有 3 个 class ,分别是 Man,Woman,Person。 其中 Man 和 Woman 都继承自 Person 。

Person 类实现了 load 方法


//Person.m
#import "Person.h"

@implementation Person

+(void)initialize{
    NSLog(@"call person initialize");
}

+(void)load{
    NSLog(@"call person load");
}

// main.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Man.h"
#import "Woman.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];

    }
    return 0;
}

@end

运行程序,查看控制台输出,可以看出 load 方法调用在 initialize 方法之前。

call person load
call person initialize

接下来修改 main.m 实现

#import <Foundation/Foundation.h>
#import "Person.h"
#import "Man.h"
#import "Woman.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
        Man *m = [[Man alloc] init];
        Woman *w = [[Woman alloc] init];
    }
    return 0;
}

运行程序,查看控制台输出,可以看出子类没有实现 load 方法的时候,runtime 不会自动调用父类的 load 方法实现

call person load
call person initialize
call man initialize
call woman initialize

接下来修改 Man.m 和 Woman.m 实现


// Man.m
#import "Man.h"

@implementation Man
+(void)initialize{
    NSLog(@"call man initialize");
}

+(void)load{
    NSLog(@"call man load");
}
@end

// Woman.m
#import "Woman.h"

@implementation Woman

+(void)initialize{
    NSLog(@"call woman initialize");
}

+(void)load{
    NSLog(@"call woman load");
}

@end

运行程序,查看控制台输出,可以看出,我们没有在 Woman 和 Man 中显式调用父类的 load 方法,但是父类的 load 方法调用都在子类之前,这个和 initialize 方法是一样的,毕竟是要先有父类初始化,才会有子类初始化。

call person load
call woman load
call man load
call person initialize
call man initialize
call woman initialize

接下来新建一个 Man 的 category 叫做 Man(Work),

// Mam + Work.m
#import "Man+Work.h"
@implementation Man (Work)
+(void)load{
    NSLog(@"call Man (Work) load");
}
@end

运行程序,查看控制台输出,可以看出 category 的 load 方法调用总是在 class 的 load 方法调用之后。

call man load
call Man (Work) load
call man initialize

总结

通过一些代码例子和官方文档,我们可以知道 initialize 方法的作用是在 class 收到第一个消息之前初始化 class。load 方法的调用时机是在 class 或者 category 被添加到 runtime 的时候。load 方法调用在 initialize 方法之前。

参考

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