方法的本质 - 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
函数
-
objc_msgSend
函数默认包含两个参数, 第一个参数为reveiver
即消息的接收者, 第二个参数SEL
,即方法名,如若还有其他参数,依次排列。 - 第一个参数根据
isa
找到类对象,优先查找cache_t
缓存,即快速查找,如果命中则返回方法 - 如果
cache_t
缓存中无此方法,会通过rw_t
进行方法列表继续遍历查找,如果找到则返回,同时更新cache_t
缓存;如果找不到,会再根据superClass
依次按着继承链查找,如果找到,会更新到当点类对象的cache_t
中,并返回,这个过程属于慢速查找(根据方发表是否排序判断,如果已排序会进行二分查找,如果未排序则直接遍历查找) - 如果以上都没能找到对应方法,进行动态方法解析,这里会做一次判断,当前不是元类时执行
resolveInstanceMethod
函数,否则执行resolveClassMethod
和resolveInstanceMethod
函数,内部还是基于objc_msgSend
,检查是否可以响应回调函数resolveInstanceMethod
或resolveClassMethod
方法,如果我们有实现resolveInstanceMethod
或resolveClassMethod
方法来动态的添加方法的实现,则返回方法的同时,更新cache_t
缓存这里所说的动态添加方法实现,指的是利用 runtime 动态添加方法,首先是添加到类对象的方法列表中(
rw_t
),至于是否会写入cache_t
缓存,取决于被调用 - 如果未做动态方法解析,最终会调用
___forwarding___
函数进入快速转发阶段,并执行forwardingTargetForSelector
回调处理过程较为复杂,有部分汇编并且并不开源, 需要用到一些断点、汇编、逆向的技能,也会用到一些逆向工具如 Hopper, IDA
- 如果快速转发阶段未能找到消息的接收者(即
forwardingTargetForSelector
返回nil
),将会进入慢速转发阶段,并执行methodSignatureForSelector
和forwardInvocation
回调 - 如果慢速转发也未能处理,最终会抛出异常