NSObject 的 initialize 和 load 方法

做为 NSObject 类中的 2 个方法 initialize 和 load 一直被咱们所熟知,可是又没有具体去深刻的了解,今天结合 Apple 官方文档,咱们来深刻了解一下 initialize 和 load 方法 。objective-c

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 。app

#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 方法代码以下:ui

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

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

咱们运行程序获得以下输出this

call person initialize
复制代码

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

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;
}
复制代码

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

call person initialize
 call person initialize
 call person initialize
复制代码

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

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

那若是 Person子类 Man 和 Woman 都实现 initialize 方法呢?cdn

@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/
相关文章
相关标签/搜索