OC 的 load 和 initialize

Posted by KentonYu on 2016-06-16

其实有时间还是需要多补充下基础知识的,毕竟步子跨太大会扯到裆。

- load

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

这是 Apple Doc 上对 - load 的描述。(强行秀一波翻译,其实我是个英语渣,最好还是看英文吧)当一个类或分类被添加到运行时的时候会触发这个方法;实现这个方法可以在加载这个类时执行一些特定的操作。

写下 demo 看看这个方法在继承树上是什么执行顺序,虽然文档里有写。

  • A class’s +load method is called after all of its superclasses’ +load methods.
  • A category +load method is called after the class’s own +load method.

先执行所有的父类的 - load 方法,分类的 - load 方法将会是最后执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Parent.m
+ (void)load {
    NSLog(@"Parent load");
}

// Child.m
+ (void)load {
    NSLog(@"Child load");
}

// Child+Category.m
+ (void)load {
    NSLog(@"Child Category load");
}

load

有兴趣的朋友可以自己去试试。

- initialize

Initializes the class before it receives its first message.

在收到第一条消息之前初始化类,也就是说你 #import 了这个类,但是没有用到这个类,就不会初始化它(有点懒加载的味道)。

The runtime sends the initialize message to classes in a thread-safe manner. Superclasses receive this message before their subclasses. The superclass implementation may be called multiple times if subclasses do not implement initialize—the runtime will call the inherited implementation—or if subclasses explicitly call [super initialize].

runtime 会线程安全地发送 initialize 消息给类。父类会比它的子类先收到这个消息。如果子类没有实现 initialize 这个方法,那么父类的 initialize 方法会被执行多次 —— 否则 runtime 将会调用子类实现 —— 除非子类明确调用 [super initialize] 。

demo time。动手 ba 一 ba 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Parent.m
+ (void)initialize {
    NSLog(@"Parent initialize");
}

// Child.m
+ (void)initialize {
    NSLog(@"Child initialize");
}

// Child+Category.m
+ (void)initialize {
    NSLog(@"Child Category initialize");
}

// main.m
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        [Child new];
        [Child new];
    }
    return 0;
}

initialize

结果中可以看出分类中的 initialize 会覆盖掉类中 initialize 方法,而父类的 initialize 方法仍旧会被执行。
接下来我们把子类中的 initialize 方法注释掉,重新跑一遍看看结果。

initialize

发现父类的 initialize 方法被调用了两遍,然后我们看文档中的这段话。

The runtime sends initialize to each class in a program just before the class, or any class that inherits from it, is sent its first message from within the program.

这段话的大致意思就是说 runtime 会发送 initialize 给这个类以及这个类的所有父类,因此当我们使用 Child 的时候,runtime 会先发送 initialize 消息给 Parent ,然后发送给 Child ,但是 Child 没有实现 initialize 方法,所以就再次执行了 Parent 的 initialize 。
所以如果使用一个 Grandson (没有实现 initialize),则 Parent 的 initialize 将会被调用三次。

有时候我们可能需要这样的效果,但是大多数时间我们是不需要这样的,因此我们可以在实现 initialize 时,这样写来避免这个问题。

1
2
3
4
5
+ (void)initialize {
  if (self == [ClassName self]) {
    // ... do the initialization ...
  }
}

用处(欢迎补充)

  • load
    在 load 中实现Method Swizzling:
1
2
3
4
5
6
7
8
9
10
11
12
+ (void)load {
    Method a = class_getInstanceMethod([self class], @selector(logA));
    Method b = class_getInstanceMethod([self class], @selector(logB));
    method_exchangeImplementations(a, b);
}
- (void)logA {
    NSLog(@"A");
}

- (void)logB {
    NSLog(@"B");
}
  • initialize
    初始化一些静态对象
1
2
3
4
5
6
7
// static NSArray *array = @[@1, @2]; //这样是不行滴,编译器不接受,只有数据类型才能这样哦
static NSArray *array;
+ (void)initialize {
if (self == [Parent class]) {
array = @[@1, @2];
}
}

小结

load initialize
调用时机 类被添加到 runtime 时 类第一次接收到消息之前
调用顺序 父类->子类->分类 父类 -> 子类(分类)
调用次数 1次 多次
显式调用父类
分类中的实现 类和分类都执行 覆盖类的实现,执行分类的实现
线程安全 安全 安全

上表主要就是这两个方法的特点,因为是线程安全的,所以在这两个方法尽量少执行复杂的操作,防止阻塞线程。

以上就是我对这两个方法的描述,如有不对望指正,给个喜欢以示支持。