思考:
- load、initialize方法的区别什么?它们在category中的调用的顺序?以及出现继承时他们之间的调用过程?
+initialize 方法会在类第一次接收到消息时调用
定义 Persion、Student 及其分类
|
|
编译顺序:
打印结果:
objc4源码解读过程
objc-msg-arm64.s
objc_msgSendobjc-runtime-new.mm
class_getInstanceMethod
lookUpImpOrNil
lookUpImpOrForward
_class_initialize
callInitialize
objc_msgSend(cls, SEL_initialize)
[Persion alloc] 的本质是:
[Student alloc] 的本质是:
objc_msgSend()
打开 runtime 源码 objc4-781,搜索 objc_msgSend(),会发现 objc_msgSend() 方法的源码是汇编语言。
objc_msgSend() - 类方法调用流程:
isa -> 类对象/元类对象,寻找方法,调用
superclass -> 类对象/元类对象,寻找方法,调用
superclass -> 类对象/元类对象,寻找方法,调用
从“寻找方法”入手👇
class_getClassMethod
class_getClassMethod 是获取类方法的函数,内部调用的是 class_getInstanceMethod 获取对象方法的函数。
class_getInstanceMethod
Jump To Definition -> class_getInstanceMethod
lookUpImpOrForward
在 class_getInstanceMethod 方法中,调用 lookUpImpOrForward(nil, sel, cls, LOOKUP_RESOLVER) 方法。所以 behavior == LOOKUP_RESOLVER
Jump To Definition -> lookUpImpOrForward
initializeNonMetaClass
initializeNonMetaClass 方法使用递归的方式优先将传入的类 cls 的父类调用 initializeNonMetaClass 方法进行处理。
Jump To Definition -> initializeNonMetaClass
设置正在调用 +initialize 方法的状态 RW_INITIALIZING:
设置已经调用 +initialize 方法的状态 RW_INITIALIZED:
isInitializing()、setInitializing()、isInitialized()、setInitialized() 方法:
callInitialize
向传入的类 cls 发送“initialize”消息。至此实现了 +initialize 方法的调用。
Jump To Definition -> callInitialize
调用顺序
|
|
编译顺序:
打印结果:
优先调用父类的 +initialize 方法
从 initializeNonMetaClass 方法中有个一递归设计,跟 +load 方法调用中的 schedule_class_load 方法如出一辙。会先判断父类的 +initialize 方法是否已经调用,如果没有会优先先调用父类的 +initialize 方法。在将所有的父类都处理完后,再调用传入的类 cls 的 +initialize 方法。
每个类只会调用一次 +initialize 方法
因为有 !cls->isInitialized() 判断,所以每个类只会初始化1次,也就是每个类只会调用一次 +initialize 方法。
父类的 +initialize 可能会被调用多次
如果子类没有实现 +initialize 会调用父类的 +initialize(所以父类的 +initialize 可能会被调用多次)。这一点通过 callInitialize 方法可以看出,向传入的类 cls 发送一条 @selector(initialize) 消息。类 cls 会通过 isa 找到 cls 元类对象查找 +initialize 方法,如果没有再通过 superclass 指针找到父类的元类方法查找 +initialize 方法,找到后并调用。
总结
load、initialize 方法的区别什么?
1、调用方式
1> +load 是根据函数地址直接调用( (*load_method)(cls, @selector(load)));
2> +initialize 是通过 objc_msgSend 调用;2、调用时刻
1> +load 是 runtime 加载类、分类的时刻调用,每个类只会调用一次;
2> +initialize 是类第一次收到消息的时候调用,每个类只会调用一次,父类的 +initialize 方法可能会被调用多次;+load 和 +initialize 的调用顺序?
+load 方法:
1、先调用类的 +load
1> 先编译的类,优先调用 +load;
2> 先调用父类的 +load,再调用子类的 +load;2、再调用分类的 +load
1> 先编译的分类,优先调用 +load;+initialize 方法:
1、先初始化父类,再初始化子类;
2、子类没有 +initialize 方法时会调用父类的 +initialize 方法;
3、有分类实现 +initialize 方法的,只调用最后被编译的分类里的 +initialize 方法;出现继承时他们之间的调用过程?
+load 方法:
1、+load 是先调用父类的 +load,再调用子类的 +load。调用 +load 方法时是通过找到每个类 +load 方法的函数地址调用的。
2、+initialize 也是先调用父类,再调用子类的。调用 +initialize 方法是通过 objc_msgSend 调用的。类收到 objc_msgSend 消息后,通过 isa 指针找到元类对象,如果没有再通过 superclass 指针找到父类的元类对象查找。