load 和 initialize 方法的执行顺序以及类和对象的关系

来源:互联网 发布:linux tmp 清理 编辑:程序博客网 时间:2024/05/16 07:36

先了解一下应用启动之后,做了什么。

main.m 中的 main() 是程序的入口,但在进入 main 函数之前,程序就执行了很多代码(不然也不会启动那么久)。

启动后执行顺序:


将程序依赖的动态链接库加载进内存

加载可执行文件中的所有符号、代码

runtime 解析被编译过的符号代码,遍历所有 Class,按继承层级依次调用Class 的 load 方法和其 Category 的 load 方法。


load 方法的执行顺序


首先来做点测试,来看看 load 方法的执行顺序。

先建一个 Single View Application。展开 Build Phases 的 Compile Sources,如下图:



在每个类的 @implementation 里加上


+(void)load{

    NSLog(@"%s",__func__);

}


来看看每个类的 load 方法的调用顺序(main.m 我做了特殊处理)。以下是运行结果:


    JMTestDemo[14939:1769791] +[ViewControllerload]

    JMTestDemo[14939:1769791] +[AppDelegateload]

    JMTestDemo[14939:1769791] +[ClassMainload]


顺序和 Compile Sources 顺序一致,目前来看,Compile Sources 的顺序就是 load 方法的调用顺序。


再来一个测试,依次添加 ClassFather,以及它的子类 ClssSon,以及另一个 ClassA。结果 Compile Sources 的顺序和我想象中的不一样,如下图



看起来毫无规律啊。(这是一个问题,先记录一下)


这个时候我改变顺序,将 ClassSon 移动到最前面,ClassFather 移动到最后面。如图



下面是运行结果:


  JMTestDemo[15034:1788736] +[ClassFatherload]

    JMTestDemo[15034:1788736] +[ClassSonload]

    JMTestDemo[15034:1788736] +[ViewControllerload]

    JMTestDemo[15034:1788736] +[AppDelegateload]

    JMTestDemo[15034:1788736] +[ClassAload]

    JMTestDemo[15034:1788736] +[ClassMainload]


也就是本应先执行 ClassSon load 方法,但先执行了 ClassFather load 方法,再来一次测试。

我将 ClassSon 里的 load 方法注释掉,以下是运行结果:


  JMTestDemo[15055:1791953] +[ViewControllerload]

    JMTestDemo[15055:1791953] +[AppDelegateload]

    JMTestDemo[15055:1791953] +[ClassAload]

    JMTestDemo[15055:1791953] +[ClassMainload]

    JMTestDemo[15055:1791953] +[ClassFatherload]


也就是说,只有在你重写了 load 方法的时候,会在执行你的 load 之前,当父类未加载时,先执行父类的 load 方法。


再来一个分类的情况,分类就很有意思了。先添加了 ClssSon+category,ClssSon+category2,ClassFather+category。将它们移动至最上方。如图



运行结果


  JMTestDemo[15181:1808284] +[ClassFatherload]

    JMTestDemo[15181:1808284] +[ClassSonload]

    JMTestDemo[15181:1808284] +[ViewControllerload]

    JMTestDemo[15181:1808284] +[AppDelegateload]

    JMTestDemo[15181:1808284] +[ClassAload]

    JMTestDemo[15181:1808284] +[ClassMainload]

    JMTestDemo[15181:1808284] +[ClassSon(category2)load]

    JMTestDemo[15181:1808284] +[ClassFather(category)load]

    JMTestDemo[15181:1808284] +[ClassSon(category)load]


明明应该最早执行 load 方法的分类,却统统最后执行,甚至晚于和它没有啥关系的 ClassMain load。所以分类低人一等,最晚执行 load 方法。


得出结论,load 的执行顺序满足以下几条


  • 执行子类的 load 之前,当父类未加载时,先执行父类的 load 方法。

  • 分类的 load 方法统一在最后执行

  • 优先满足以上两条,再满足按 Compile Sources 的顺序执行 load 方法。


initialize 方法的执行顺序


这个时候来测试 initialize 的执行顺序,在 ClassFather ClassSon 以及他们的分类都重写 initialize 方法,并将 ClassFather 移动至 ClassSon 的前面,在 ClassFather load 里添加调用 ClassSon 的类方法的代码,如下


+(void)load{

    NSLog(@"%s",__func__);

    [ClassSonmethod];

}


运行结果如下


  JMTestDemo[15325:1836741] +[ClassFatherload]

    JMTestDemo[15325:1836741] +[ClassFather(category)initialize]

    JMTestDemo[15325:1836741] +[ClassSon(category)initialize]

    JMTestDemo[15325:1836741] +[ClassSonmethod]

    JMTestDemo[15325:1836741] +[ClassSonload]

    JMTestDemo[15325:1836741] +[ViewControllerload]

    JMTestDemo[15325:1836741] +[AppDelegateload]

    JMTestDemo[15325:1836741] +[ClassAload]

    JMTestDemo[15325:1836741] +[ClassMainload]

    JMTestDemo[15325:1836741] +[ClassSon(category2)load]

    JMTestDemo[15325:1836741] +[ClassSon(category)load]

    JMTestDemo[15325:1836741] +[ClassFather(category)load]


从运行结果来看,先执行了 ClassFather(category) initialize,再执行了 ClassSon(category) initialize,而 ClassSon load 在后面执行。


也就是说 load 方法还未执行也不会影响到这个类的使用。


另一个现象是执行子类 initialize 的时候会先执行其父类的 initialize。且 category 的覆写效应对 load 方法无效,但对 initialize 方法有效。且按 Complile Sources 的顺序,ClassSon(category2) 先覆写了 ClassSon 的 initialize 方法,接着 ClassSon(category) 覆写了 ClassSon(category2) 的 initialize。


如果将子类以及类别的 initialize 注释掉,再修改 ClassFather(category) initialize ,如下


+(void)initialize{

    NSLog(@"调用者:%@ 调用方法:%s",NSStringFromClass(self),__func__);

}


结果如下


  JMTestDemo[15458:1863222] +[ClassFatherload]

    JMTestDemo[15458:1863222]调用者:ClassFather调用方法:+[ClassFather(category)initialize]

    JMTestDemo[15458:1863222]调用者:ClassSon调用方法:+[ClassFather(category)initialize]

    JMTestDemo[15458:1863222] +[ClassSonmethod]


也就是子类会继承父类的 initialize 。当执行完父类的 initialize 方法,准备执行子类的 initialize 方法时,会根据继承链找到父类的 initialize 执行。为了防止重复执行 initialize 里的代码,可以根据调用者来决定是否执行 initialize 里的其它代码。


类和对象


这块写了一部分,但查资料的时候查到写的非常不错的,我觉得我没有写的必要了,留下链接,强烈建议想对 iOS 开发中的类和对象有更深了解的人看看。


从 NSObject 的初始化了解 isa

http://draveness.me/isa/

深入解析 ObjC 中方法的结构

http://draveness.me/method-struct/


0 0
原创粉丝点击