objc_msgSend做了什么事情

以下所有内容属笔者原创, 如有雷同纯属巧合, 未经允许不得转载.

OC中的方法调用实质是发送消息(objc_msgSend())

objc_msgSend()方法, 默认有2个必传参数:

  • 接收者
  • SEL选择器

objc_msgSend方法的底层, 苹果是用汇编语言实现的, 发送消息以后, 主要是查找方法, 查找方法的步骤是:

  1. 消息发送: 从实例对象对应的Class或者Meta Class中查找方法
  2. 动态方法解析: 如果步骤1没有找到方法, 会开始进行resolve, 动态解析. 在这个过程中, 如果开发者手动添加了方法实现, 那么会将方法添加到相应的ClassMeta Class中, 然后重新进行步骤1
  3. 消息转发: 如果步骤2执行结束之后, 也没有找到方法, 开始进行方法转发(___forwarding___ 方法), 若到此还没有查找到方法, 就会报错不识别方法选择器

一. 从实例对象对应的Class或者Meta Class中查找方法

实例对象对应的ClassMeta Class对象实际结构是一样的, 只是存储内容的差别而已

实例对象或者类对象调用方法以后, 会将方法缓存到对应的ClassMeta Class中.

苹果特意在此添加了缓存, 提高查找速度. 并且, 不同于普通的forwhile循环遍历, 苹果针对这片缓存区采用了散列算法, 通过空间换时间的方式, 来大大提高了查找效率.

  1. 缓存查找: 当发送消息以后, 马上从Class中开始查找方法, 并且优先从Cache中查找
  2. 方法列表查找: 若当前Class对应的Cache中找不到方法, 开始从Class对应的方法列表中查找
  3. Super Class查找: 当前实例对象的ClassMeta Class中未查找到方法以后, 会通过ClassMeta Classisa指针, 找到对应的父类, 继续进行上述2个步骤, 直至找到方法

二. 动态方法解析

第一次无法正常从Cache方法列表中查到方法, 则会进行动态方法解析. 一定注意, 这里说的是第一次.

动态解析:

  1. 若调用的是实例方法, 系统会调用- (void)resolveInstanceMethod:(SEL)sel方法, 我们可以在这里, 对sel进行判断, 从而利用runtime的特性动态添加实例方法
  2. 若调用的是类方法, 系统会调用- (void)resolveClassMethod:(SEL)sel方法, 同样可以在这里, 对sel进行判断, 利用runtime动态添加类方法
  3. - (void)resolveInstanceMethod:(SEL)sel- (void)resolveClassMethod:(SEL)sel方法中动态添加相应的实例方法类方法之后, 这个方法实际上被会添加到对应的ClassMeta Class的方法列表中
  4. 此时, 系统会执行retry操作, 第二次CacheClass方法列表中查找方法, 并缓存到Cache中. (注意这里说的第二次)

三. 消息转发

若经过动态方法解析以后, 都没有找到对应的方法, 那么系统会进行最后一步操作, 叫做消息转发

消息转发会先后调用以下几个方法:

  1. forwardingTargetForSelector: 返回值不为nil, 调用objc_msgSend(返回值, SEL); 返回值为nil, 执行下一步
  2. methodSignatureForSelector: 返回值不为nil, 调用forwardInvocation:方法; 返回值为nil, 执行下一步
  3. doesNotRecognizeSelector:

你可能感兴趣的:(objc_msgSend做了什么事情)