以下所有内容属笔者原创, 如有雷同纯属巧合, 未经允许不得转载.
OC中的方法调用实质是发送消息(objc_msgSend()
)
objc_msgSend()
方法, 默认有2个必传参数:
- 接收者
- SEL选择器
objc_msgSend
方法的底层, 苹果是用汇编语言实现的, 发送消息以后, 主要是查找方法, 查找方法的步骤是:
-
消息发送
: 从实例对象对应的Class
或者Meta Class
中查找方法 -
动态方法解析
: 如果步骤1
没有找到方法, 会开始进行resolve
, 动态解析. 在这个过程中, 如果开发者手动添加了方法实现, 那么会将方法添加到相应的Class
或Meta Class
中, 然后重新进行步骤1
-
消息转发
: 如果步骤2
执行结束之后, 也没有找到方法, 开始进行方法转发(___forwarding___ 方法
), 若到此还没有查找到方法, 就会报错不识别方法选择器
一. 从实例对象对应的Class
或者Meta Class
中查找方法
实例对象对应的Class
和Meta Class
对象实际结构是一样的, 只是存储内容的差别而已
当实例对象
或者类对象
调用方法以后, 会将方法缓存到对应的Class
或Meta Class
中.
苹果特意在此添加了缓存, 提高查找速度. 并且, 不同于普通的for
或while
循环遍历, 苹果针对这片缓存区采用了散列
算法, 通过空间换时间的方式, 来大大提高了查找效率.
-
缓存查找
: 当发送消息以后, 马上从Class
中开始查找方法, 并且优先从Cache
中查找 -
方法列表查找
: 若当前Class
对应的Cache
中找不到方法, 开始从Class
对应的方法列表中查找 -
Super Class查找
: 当前实例对象的Class
或Meta Class
中未查找到方法以后, 会通过Class
或Meta Class
的isa指针
, 找到对应的父类, 继续进行上述2个步骤, 直至找到方法
二. 动态方法解析
若第一次
无法正常从Cache
和方法列表
中查到方法, 则会进行动态方法解析. 一定注意, 这里说的是第一次.
动态解析:
- 若调用的是实例方法, 系统会调用
- (void)resolveInstanceMethod:(SEL)sel
方法, 我们可以在这里, 对sel
进行判断, 从而利用runtime
的特性动态添加实例方法 - 若调用的是类方法, 系统会调用
- (void)resolveClassMethod:(SEL)sel
方法, 同样可以在这里, 对sel
进行判断, 利用runtime
动态添加类方法 - 在
- (void)resolveInstanceMethod:(SEL)sel
或- (void)resolveClassMethod:(SEL)sel
方法中动态添加相应的实例方法
或类方法
之后, 这个方法实际上被会添加到对应的Class
或Meta Class
的方法列表中 - 此时, 系统会执行
retry
操作, 第二次从Cache
和Class
方法列表中查找方法, 并缓存到Cache
中. (注意这里说的第二次)
三. 消息转发
若经过动态方法解析以后, 都没有找到对应的方法, 那么系统会进行最后一步操作, 叫做消息转发
消息转发
会先后调用以下几个方法:
-
forwardingTargetForSelector:
返回值不为nil, 调用objc_msgSend(返回值, SEL)
; 返回值为nil, 执行下一步 -
methodSignatureForSelector:
返回值不为nil, 调用forwardInvocation:
方法; 返回值为nil, 执行下一步 doesNotRecognizeSelector: