方法的本质 - ShenYj/ShenYj.github.io GitHub Wiki

方法的本质

  • 方法的本质:发送消息,消息会有以下几个流程

    • 快速查找(objc_msgSend) - cache_t缓存消息中查找
    • 慢速查找 - 递归自己|父类 - lookUpImpOrForward
    • 查找不到消息:动态方法解析 - resolveInstanceMethod
    • 消息快速转发 - forwardingTargetForSelector
    • 消息慢速转发 - methodSignatureForSelector & forwardInvocation
  • sel是方法编号 在read_images期间就编译进了内存

  • imp是函数实现指针 ,找imp就是找函数的过程

    sel 相当于 一本书的目录title , imp 相当于 书本的页码

实例对象方法的调用过程

当我们通过一个实例对象调用某个方法时,最终转成 C++ 代码可以发现其本质是 objc_msgSend 函数

  1. objc_msgSend 函数默认包含两个参数, 第一个参数为 reveiver 即消息的接收者, 第二个参数 SEL,即方法名,如若还有其他参数,依次排列。
  2. 第一个参数根据 isa 找到类对象,优先查找 cache_t 缓存,即快速查找,如果命中则返回方法
  3. 如果 cache_t 缓存中无此方法,会通过 rw_t 进行方法列表继续遍历查找,如果找到则返回,同时更新 cache_t 缓存;如果找不到,会再根据 superClass 依次按着继承链查找,如果找到,会更新到当点类对象的 cache_t 中,并返回,这个过程属于慢速查找(根据方发表是否排序判断,如果已排序会进行二分查找,如果未排序则直接遍历查找)
  4. 如果以上都没能找到对应方法,进行动态方法解析,这里会做一次判断,当前不是元类时执行resolveInstanceMethod函数,否则执行resolveClassMethodresolveInstanceMethod函数,内部还是基于 objc_msgSend,检查是否可以响应回调函数 resolveInstanceMethodresolveClassMethod 方法,如果我们有实现 resolveInstanceMethodresolveClassMethod 方法来动态的添加方法的实现,则返回方法的同时,更新 cache_t 缓存

    这里所说的动态添加方法实现,指的是利用 runtime 动态添加方法,首先是添加到类对象的方法列表中(rw_t),至于是否会写入 cache_t 缓存,取决于被调用

  5. 如果未做动态方法解析,最终会调用 ___forwarding___ 函数进入快速转发阶段,并执行 forwardingTargetForSelector 回调

    处理过程较为复杂,有部分汇编并且并不开源, 需要用到一些断点、汇编、逆向的技能,也会用到一些逆向工具如 Hopper, IDA

  6. 如果快速转发阶段未能找到消息的接收者(即 forwardingTargetForSelector 返回 nil),将会进入慢速转发阶段,并执行 methodSignatureForSelectorforwardInvocation 回调
  7. 如果慢速转发也未能处理,最终会抛出异常
⚠️ **GitHub.com Fallback** ⚠️