分享一些我对 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
已经被调用过了。