首先了解一下应用启动后,做了什么
main.m 中的 main() 是程序的入口,但在进入 main 函数之前,程序就执行了很多代码(不然也不会启动那么久)。
将程序依赖的动态链接库加载进内存
加载可执行文件中的所有符号、代码
runtime 解析被编译过的符号代码,遍历所有 Class,按继承层级依次调用Class 的 load 方法和其 Category 的 load 方法。
详细的步骤可以参考sunnyxx大神的这篇文章;
Load
Load方法在文件被程序装载时调用.也就是在Compile Sources中出现的文件.与<em>这个类是否被用到无关</em>.
调用规则
<pre>
2016-11-11 14:28:39.807 [1734:818024] +[FatherViewController load]
2016-11-11 14:28:39.808 [1734:818024] +[SonViewController load]
2016-11-11 14:28:39.808 [1734:818024] +[ViewController load]
2016-11-11 14:28:39.808 [1734:818024] +[AppDelegate load]
2016-11-11 14:28:39.808 [1734:818024] +[ClassMian load]
2016-11-11 14:28:39.809 [1734:818024] +[SonViewController(Category_son) load]
2016-11-11 14:28:39.809 [1734:818024] +[FatherViewController(Category_01) load]
2016-11-11 14:28:39.809 [1734:818024] +[ViewController(Category) load]
</pre>
如果感兴趣 可以自己创建一些类和类别 展开 Build Phases 的 Compile Sources 调用下各个文件的顺序 尝试一下各种情况下load的调用顺序.主要太没技术含量了,就不详细写这些了 = =.
结论
<ul>
<li> 执行子类的 load 之前,当父类未加载时,先执行父类的 load 方法。</li>
<li>分类的 load 方法统一在最后执行</li>
<li>优先满足以上两条,再满足按 Compile Sources 的顺序执行 load 方法。</li></ul>
需要注意的是子类如果没有实现load方法,那么就不会调用父类的load方法
由于调用load方法时的环境很不安全,我们应该尽量减少load方法的逻辑。另一个原因是load方法是线程安全的,它内部使用了锁,所以我们应该避免线程阻塞load方法中。
initialize
这个方法是在第一次给某个类发送消息时调用(实例化对象),并且只会调用一次.这个类方法是惰性调用,如果一个类一直没有被用到,此方法也不会执行.
initiialze方法的执行顺序
<pre>**2016-11-11 15:19:18.029 [1850:1017547] +[FatherViewController load]** **2016-11-11 15:19:18.031 [1850:1017547] +[FatherViewController(Category_01) initialize]** **2016-11-11 15:19:18.031 [1850:1017547] +[SonViewController(Category_son) initialize]** **2016-11-11 15:19:18.031 [1850:1017547] +[SonViewController load]** **2016-11-11 15:19:18.031 [1850:1017547] +[ViewController load]** **2016-11-11 15:19:18.031 [1850:1017547] +[AppDelegate load]** **2016-11-11 15:19:18.031 [1850:1017547] +[mianClass load]** **2016-11-11 15:19:18.032 [1850:1017547] +[SonViewController(Category_son) load]** **2016-11-11 15:19:18.032 [1850:1017547] +[FatherViewController(Category_01) load]** **2016-11-11 15:19:18.032 [1850:1017547] +[ViewController(Category) load]** **2016-11-11 15:19:18.032 [1850:1017547] +[AppDelegate initialize]** **2016-11-11 15:19:18.085 [1850:1017547] +[ViewController(Category) initialize]**
</pre>
从结果来看,先执行了FatherViewController(Category_01) initialize
, 在执行了SonViewController(Category_son) initialize
,而SonViewController load
最后执行.也就是说load方法还未执行也不会影响这个类的使用.
下面来看一看initialize方法的继承问题
从上面打印结果可以看出执行子类 initialize 的时候会先执行其父类的 initialize。且 category 的覆写效应对 load 方法无效,但对 initialize 方法有效。
下面将子类的initialize注释,重写其initialze方法
<pre>+ (void)initialize { NSLog(@"调用者:%@ 调用方法:%s",NSStringFromClass(self), __func__);}
</pre>
子类会继承父类的 initialize 。当执行完父类的 initialize 方法,准备执行子类的 initialize 方法时,会根据继承链找到父类的 initialize 执行。
什么时候使用initialize
initialize方法主要用于对一些不好方便在编译期初始化的对象进行赋值,如NSMutableArray
的实例化方法,在使用此方法时需要注意此方法是被调用多次的 是否需要添加dispatch_once.
总结
1.load
和initialize
方法都会在实例化对象前调用, 以main函数为分水岭,load
在mian之前,initialize
在main之后.
2.load
和initialize
方法调用父类的方法是隐性的自动调用,即使子类没有实现initialize方法也会调用父类的方法,load
方法则不会调用父类.
3.load方法常常用于黑魔法的实现,initialize方法用于初始化全局变量
4.load initialize方法都是线程安全的!需避免复杂操作.
网友评论