iOS中消息机制
OC语言是C语言的超集,C语言有个特点就是在编译期间就已经确定了对应的被调用的方法,行业术语是以静态绑定的方式进行地址定位,而调用方法以硬编码的形式存储,某种程度上也侧面说明其运行效率高。而OC语言的方法调用采用向对象发送消息的形式进行,所以对于方法的调用并不能在编译期间确定方法的所处内存地址。
给对象发送消息是通过objc_msgSend(_cmd, sel, ...)方法进行的,所以每个对象接收者都会存一张方法表,如果该表中含有存储的方法,此时会直接跳到方法实现中执行相应的方法体。假如每次都查找类中所存储的方法表,那么势必会影响方法执行的效率。为此,OC在每个类中都添加了一个方法缓存表(fast map),这样就能将第一次查找的方法存储在缓存表里,下次需要用的时候直接从缓存表里直接拿到方法体,以保证正常的方法调用效率。
在理解消息转发之前,先了解一下OC中的对象
在OC中对象分为三类,分别是实例对象、类对象以及元类对象,这三类对象之间是有联系的。从isa指针指向来说的话,实例对象的isa指针指向类对象,类对象的isa指针指向元类对象,元类对象的isa指针指向基类元类对象。因为OC中类实质是一个结构体,除了isa指针,还包含superclass指针(实质都是NSObject的对象),还包含属性方法表等。类是对数据以及方法的封装,所以这三者的关系链也就由isa指针链接起来了。
网图.png
isa指针的实际应用
不如说一个场景,如果调用类Class_a中的方法method_a,此时系统会通过实例对象中的isa指针找到类对象,然后通过查找在类对象中存储的方法表,找到要执行的方法。同理,如果要调用类Class_a中的类方法class_method_a,此时系统会通过类对象中的isa指针找到当前类的元类对象,然后通过元类对象中的方法表找到对应的类方法,进而调用。
网图
所以类对象存储了实例对象的相关的属性方法信息,元类对象存储了类对象的相关的属性方法信息。调用链也是沿着类信息存储方向进行调用的。
superclass 指针的作用
类中存储的superclass是用来指向继承关系的父类对象的,依旧说场景。
网图
- 如果想要调用Student实例对象的基类Person对象方法,调用过程是什么样的呢?
首先会通过Student实例对象的
isa指针找到Student的类对象,通过查找类对象方法表存储的方法,在没有找到的情况下,通过Superclass指针找到Student的继承的类对象Person进行方法索引,找到调用其方法。
- 如果想要调用Student实例对象的
NSObject对象方法,过程如何?
首先会通过Student的实例对象的
isa指针找到Student的类对象,通过查找Student的类对象Person存储的方法表,不存在的话,再通过Person类对象中的isa指针找到Person的元类对象,最后查找存储在元类对象的属性方法表找到要调用的的方法,最后调用。
汇总
以下图为例说明
网图
- isa指向问题
- 实例对象Instance 的
isa指向类对象Class- 类对象Class的
isa指针指向元类对象meta-class- 基类的元类对象meta-class的
isa指向自己
- superclass指向问题
- 类对象class的
superclass指针指向父类的class类对象,如果没有父类,superclass指针为nil- 元类对象
meta-class的superclass指向父类的元类对象meta-class,基类的元类对象meta-class的superclass指向基类的类对象class(注意是基类的对象class)
- 实例
对象instance调用对象方法的过程链
- 通过实例对象instance的
isa指针找到类对象class,找到即调用- 如果找不到,通过实例对象的
instance中的superclass找到父类对象,通过父类对象的isa指针找到父类对象的类对象class,找到调用方法- 如果仍然找不到,那么系统会通过消息转发机制进行消息转发,走消息转发的响应链,最后如果都没有能够处理消息的方法的话,那么会抛出
unselector的异常
- 类对象调用类方法的过程链
- 通过类对象
class isa指针找到元类对象meta-class,通过存储在元类对象的方法表查找对应的方法进行调用- 如果没有找到,那么通过
class的superclass指针找到父类superclass,然后通过父类的superclass的isa指针找到父类的元类对象super-meta-class,通过存储在父类的元类对象meta-class找到类对象进行调用。
- 关于
class对象、meta-class对象的本质
class对象、meta-class对象的本质是结构体 struct objc_class.