+load方法的执行顺序你了解么?

可能+load方法应该是每个iOS开发同学都非常熟悉的方法,而且面试中+load方法相关的面试提也是非常常见,但你了解的+load方法真的跟实际上的一样么?看文章之前先思考几个问题+load方法在什么时候?+load方法是如何执行的?一个类的+load方法会执行几次?类和分类的+load方法的执行顺序?同一个类的不同分类的+load方法的执行顺序?父类和子类的+load方法的执...
摘要由CSDN通过智能技术生成

可能+load方法应该是每个iOS开发同学都非常熟悉的方法,而且面试中+load方法相关的面试提也是非常常见,但你了解的+load方法真的跟实际上的一样么?

看文章之前先思考几个问题

  1. +load方法在什么时候?
  2. +load方法是如何执行的?
  3. 一个类的+load方法会执行几次?
  4. 类和分类的+load方法的执行顺序?
  5. 同一个类的不同分类的+load方法的执行顺序?
  6. 父类和子类的+load方法的执行顺序?
  7. 没有继承关系的两个类的+load方法的执行顺序?
  8. 静态库、动态库中的+load方法与主程序的+load的执行顺序?
  9. 为什么我们在+load方法中不必写[super load]

如果有些问题不确定或者不知道,那么就往下看,我们一起来解决这些问题。

+load方法的执行时机

官方文档是这样描述的:

Invoked whenever a class or category is added to the Objective-C runtime。The load message is sent to classes and categories that are both dynamically loaded and statically linked, but only if the newly loaded class or category implements a method that can respond.

当一个类或者分类被加载到Objectie-C的Runtime运行环境中时,会调用它对应的+load方法。对于所有静态库中和动态库中实现了+load方法的类和分类都有效。

当应用启动时,首先要fork进程,然后进行动态链接。+load方法的调用就是在动态链接这个阶段进行的。动态链接结束之后,会执行程序的main函数。

dyld简介

dyld(the dynamic link editor)是苹果的动态链接器,是苹果操作系统的一个重要组成部分,在系统内核做好程序准备工作之后,交由dyld负责余下的工作。整个加载过程可细分为九步:

  1. 设置运行环境
  2. 记载共享缓存
  3. 实例化主程序
  4. 加载插入的动态库
  5. 链接主程序
  6. 链接插入的动态库
  7. 执行弱符号绑定
  8. 执行初始化方法
  9. 查找入口点并返回

各个步骤具体做的事情,请参考该博客

步骤8,执行初始化方法。如果看过dyld源码或者源码分析的,可以知道这个步骤是在initializeMainExecutable函数中完成的。dyld会有限初始化动态库,然后初始化主程序。该函数经过系列的执行会进入notifySingle方法,随后会调用到load_images方法,然后会调用到call_load_methods方法。我们之前分析过dyld,如果感兴趣请看之前发表的这篇博客。如果不想研究源码也没关系,我们随便写一个工程,新建一个类并实现+load方法,打断点定位,我们也能得到下图的调用栈。

image

所以到这里,我们得到了第一个问题的答案:+load方法会在dyld阶段的执行初始化方法中执行。

多说一点,dyld的初始化顺序:

  1. 调用所有Framework中的初始化方法
  2. 调用所有的+load方法
  3. 调用C++ 的静态初始化方法及C/C++ 中的attribute(constructor)函数
  4. 调用给所有链接到目标文件的framework中的初始化方法

+load方法的执行顺序

官方文档中提到了+load方法的执行顺序

  1. 一个类的+load方法调用在它的父类的+load方法之后
  2. 一个分类的+load方法调用在它本身类的+load方法之后
类与类之间的+load方法的执行顺序

我们写一个demo来验证一下。新建一个iOS工程,然后新建一个Person

@interface Person : NSObject

@end

@implementation Person

+ (void)load
{
    NSLog(@"---- %p %s", self, __FUNCTION__);
}

@end

然后新建一个Student类继承Person类。

@interface Student : Person

@end

@implementation Student

+ (void)load
{
    NSLog(@"---- %p %s", self, __FUNCTION__);
}
@end

再新建一个HighSchoolStudent类继承Student

@interface HighSchoolStudent : Student

@end

@implementation HighSchoolStudent

+ (void)load
{    
    NSLog(@"---- %p %s", self, __FUNCTION__);
}

@end

到这里,我们看到了一条继承链。运行程序,得到结果

---- 0x10b04c0c0 +[Person load]
---- 0x10b04c160 +[Student load]
---- 0x10b04c1b0 +[HighSchoolStudent load]

结果如预期,接下来,我们增加一个Animal

@interface Animal : NSObject

@end

@implementation Animal

+ (void)load
{
    NSLog(@"---- %p %s", self, __FUNCTION__);
}

@end

现在看一下结果

---- 0x1094930e8 +[Person load]
---- 0x109493138 +[Animal load]
---- 0x109493188 +[Student load]
---- 0x1094931d8 +[HighSchoolStudent load]

我们发现,Animal类的+load方法也调用了,但是它的调用顺序,我们还不知道是如何的。这个时

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值