NSObject 的 load 方法

218 查看

分享一些我对 NSObject+load 方法的一些理解。

调用时机

在每个类和类别初始化的时候都会调用,子类不会重载父类的 +load ,两个都会调用这个方法。

调用顺序

各个 +load 方法遵守苹果文档所说的顺序:

The order of initialization is as follows:

  • All initializers in any framework you link to.
  • All +load methods in your image.
  • All C++ static initializers and C/C++ __attribute__(constructor) functions in your image.
  • All initializers in frameworks that link to you.

In addition:

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.

但是官方的说明不是很完整,于是我用 Xcode 验证了一下,这里是一些验证结果:

我们自己连接的静态库,包括伪 framework(本质上是个静态库),实际上也属于苹果文档里说的 your image。也就是说,如果里面有实现 load 方法的类,不能保证它们总是先调用。一般来说是这样的顺序:

  • 所有我们的类的 +load
  • 所有静态库的类的 +load
  • 所有我们的类别的 +load
  • 所有静态库的类别的 +load

但是如果继承了来自静态库的类,那么按照苹果的规则,他们的父类会提前调用 +load。没有继承关系的类之间,+load 的调用顺序跟随编译顺序。编译顺序可以在 target 的 Build Phases 里面指定。类别之间,调用 +load 的顺序也是完全按照编译顺序。

依赖的处理方式

假设类 A 和类 B 都是我们的项目里的类。如果我们想在 A 调用 +load 的时候发消息给 B,可以吗?

答案是:可以的,但是不能直接发消息给 B。为什么呢?如果直接发消息给 B ,因为不知道 B 是否一定要调用 +load 才能用,所以很有可能会出错。而且这样会导致 B 的 +initialize+load 之前就调用了,也许会破坏了 B 的设计者的本意。

正确的方法应该是,等待当前的初始化函数结束了,再发消息给 B。用 GCD 来实现的话,就是这样写:

{% codeblock 安全方法 lang:objc %}
dispatch_async(dispatch_get_main_queue(), ^{
[B doSomthing];
});
{% endcodeblock %}

这样写之所以是对的,是因为这样不会马上调用 B 的 +doSomthing 方法,而是等到 RunLoop 下一次事件开始再调用。那时候整个初始化已经结束了,这样就能确保 B 的 +load 已经被调用过了。