1 OC消息机制
OC对象调用方法在编译阶段不知道具体的方法在哪里,是在运行的过程中,向对象发送消息,通过对象得到函数地址,调用函数,如果没有找到,则抛出异常。
OC中方法调用,其实都是转成了objc_msgSend函数的调用, 给receiver 【方法调用者】 发送了一条消息 【selector 方法名】。
objc_msgSend 底层有3大阶段:
- OC 消息发送
- 动态方法解析
- 消息转发
类的数据结构: 类对象中,存储着isa, superclass , 属性、对象方法、协议、成员变量等
struct objc_class {
Class isa; //类的isa指向它的metaclass
Class superclass; //指向类的超类
cache_t cache; // 方法缓存 - 方法缓存列表,散列表(哈希表)数据结构,缓存自己之前调用过的方法,指向最近使用的方法的指针,用于提升效率
class_data_bits_t; // 获取具体的类信息
}
2 OC消息发送
如何通过对象找到调用函数的地址?
[selector 方法名]会返回一个SEL变量,这个变量是char *的形式将方法对应为唯一的值,以键值对的形式存储函数地址。类对象中的isa就是指向对象的类的指针isa。
发送过程:
1、在程序运行时会给每个类的结构体分配一块空间,对象的isa指向的就是这块空间
2、当发送调用方法的消息后,根据receiver对象的isa指针获取它对应的class
3、通过SEL查找函数,优先在class的cache查找message方法,如果找不到,再到methodLists查找,若还不能找到,再去superClass中查找,找到后会将method放到对应objc_class的cache中,方便下次查找。
4、一旦找到message这个方法,再依据receiver 中的self 指针找到当前的对象,调用当前对象的具体实现的方法(IMP),然后传递参数,调用实现方法。
当对象收到无法解读的消息的时候,则需要启动消息转发。
1、首先判断调用的方法对象是否为nil, 如果为nil,则直接退出,通过实例对象的isa 找到类对象,在类对象的cache 列表中查找,如果找到直接调用,
2、如果cache 列表中没有,则在这个类对象的方法列表中查找,如果找到调用,并将此方法放在自己的cache 中,方便之后查找
3、如果类对象方法列表中没有,则通过类对象的superclass 指针,找到父类的类对象, 先cache 中查找,再方法列表中查看。如果在父类中找到了方法,则调用,并将方法缓存到自己的cache中,方便之后查找。
4、如果superclass 为nil, 则进入动态方法解析阶段。
3 OC动态方法解析
1、首先会判断是否进入过动态方法解析阶段,
- 如果进入过则直接进入下一步,消息转发
- 如果没有进入,则会进入这个方法解析阶段,会调用 - resolveInstanceMethond; 在这个方法里面实现代码,之后将这个动态方法解析阶段标记为执行过的状态。
2、动态解析过后,会重新走消息发送的流程,如果找不到方法会再次进入resolveInstanceMethond方法,此时已经标记过了,直接进入一下个阶段:消息转发
4 OC消息转发
绘图中