iOS 探讨之 ObjC类初始化
阐述
近期发现iOS黑魔法中许多神奇的操作都是在类初始化的时候,之前玩的方式都是在对象实例化的时候,感觉发现了一片新大陆玩耍一下。
探讨
NSObject 作为大多数类的基类(NSProxy特殊)我们首先去IDE中寻求与类初始化有关的信息。
在NSObject 的介绍信息中,关于类初始化这方面官方提到了两个方法: + initialize、 + load 。
也就是说我们可以在这两个方法中搞一些有"意思"的事情。
+ load
官方在开篇直接指出:
类文件或类幕文件在加入到runtime(个人理解为应用运行时,编译、链接之后的阶段)时,该类文件或类幕文件的 + load 方法会被调用。
注意: 只有实现了 + load 方法的类文件或类幕文件在类加载时才会被调用。
↓↓↓ 官方标注 ↓↓↓
复杂应用文件间类初始化顺序:(都符合 + load 调用的条件)
1 开发者引用的框架中的文件
2 开发者编写的OC文件
3 所有C++静态初始化文件和C/C++ __attribute__(constructor) 声明的函数
4 其它引用开发者编写文件的框架
类系文件(子类、父类、类幕)的类初始化顺序:
1 先初始化所有父类,再初始化子类
2 所有类幕文件在所有类初始化完后初始化
3 每个类文件中的 + load 方法只会执行一次
4 在以上条件下,在Build Phases - Compile Sources 顺序越靠上初始化越早
Tip:
多个Category里都实现了 + load 方法,那么这些方法都会被调用一次。这个特性决定了+load是干坏事的绝佳场所,比如 swizzling。
+ initialize
官方用简短的一句对 initializes 进行介绍:
在类接收第一条信息的时候对其进行初始化。
应用文件间类初始化的顺序:
1 与类接收到消息的先后有关
类系文件(子类、父类、类幕)的类初始化顺序
1 先初始化父类,再初始化子类
2 若有类幕文件只会初始化类幕文件,不会初始化当前类文件
3 类幕文件只会初始化一个,并且是在Build Phases - Compile Sources 顺序越靠下的文件
4 每个类只会初始化一次(父类 + initialize 有可能会被多次调用)
父类 + initialize 被调多次场景:
1 子类没有实现 initialize 方法
2 子类实现了 initialize 方法,且在实现中执行了 [super initialize] 语句
阻止父类 + initialize 方法执行多次方法:
+ load 与 + initialize 对比:
1 + load 方法会在Class被加载的时候调用,这个时间很早,所有对于需要很早被执行的代码来说是很有用的。
注意:这个时候整体环境尚未初始化完成,调用代码可能出错.
2 + initialize 方法lazy触发,对于初始化设置的环境友好很多。只要不是在类接收第一条消息之前一定要做的事情,都可以在这个方法里面处理。
注意:因为 initialize 是以阻塞的形式执行,故需要限制 initialize 中的任务数量。并且不要在任务中添加锁(Lock),防止死循环的产生。
资料