OC底层原理探索之消息转发流程

425 阅读3分钟

instrumentObjcMessageSends

上一篇有讲到这个方法,那么这个方法的由来? lookUpImpOrForward->log_and_fill_cache -> objcMsgLogEnabled,全局搜索,发现默认的objcMsgLogEnabled=false,在log_and_fill_cache中,只有当objcMsgLogEnabled=true的时候才会打印方法,所以我们找到全局中对objcMsgLogEnabled赋值的地方,这里就定位到了voidinstrumentObjcMessageSends(BOOL flag)这个方法里面

快速转发forwardingTargetForSelector

image.png 上面的代码在Person里没有实现sayHello的方法,而是新增了forwardingTargetForSelector:的方法指向了student内部实现的sayHello的方法,此时就没有报错了 image.png 如果此时没有指定新的消息的接收者也就是forwardingTargetForSelector:return = nil,那么就来到了下面

慢速转发methodSignatureForSelector

下面这两个方法要搭配使用forwardInvocation这个方法可实现,可不实现

- (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");// 可处理 可不处理
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");

image.png image.png

反汇编定位消息转发流程

在报错的时候控制台输入btXcode会自动输出最后一次的调用堆栈 image.png 可以知道在[NSObject(NSObject) doesNotRecognizeSelector:]main之间又调用了两个比较重要的函数,经过多次查找最终在CoreFoundation的动态库里面找到。使用Hopper打开,定位到伪代码 image.png image.png 我们去到__forwarding__的方法里面 image.png 我们肯定聚焦在没有实现这一侧,好的,那就跳转到64a67这里,可以看到这里也定位到了。 image.png 上面的反汇编的这些流程也佐证了上面讲的消息转发。

resolveInstanceMethod方法走两次

image.png 打开Person里面的消息转发流程的那几个方法,打印 image.png 在方法的慢速转发之后,**_forwardStackInvocation**这个sel再次调用了**[Person resolveInstanceMethod:]**,那么这个sel到底是什么呢?回到Hopper里面,在__forwarding__的方法里面 image.png 看伪代码,如果rax不存在,里明显是没有实现的,定位到64c19 ​image.png 1.正常情况下是没有实现的,回到64e2c,最后定位到了这里 image.png 2.如果实现了,也就是上面我们写的例子,继续往下走 image.png 看到调用了forwardInvocation的方法,也就是上面控制台日志最后的一个方法,最后也回到了方法1的那个方法。但是____forwarding___.cold.4()具体的实现我们看不到,所以我们可以认为是系统在慢速转发之后触发了**_forwardStackInvocation:**方法,继续走了一遍查找流程,这也就是为什么**resolveInstanceMethod:**方法走两次的原因。 ​

image.png

补充

lldb调试命令

命令解释
break NUM在指定的行上设置断点。
bt显示所有的调用栈帧。该命令可用来显示函数的调用顺序。
clear删除设置在特定源文件、特定行上的断点。其用法为:clear FILENAME:NUM。
continue继续执行正在调试的程序。该命令用在程序由于处理信号或断点而导致停止运行时。
display EXPR每次程序停止后显示表达式的值。表达式由程序定义的变量组成。
file FILE装载指定的可执行文件进行调试。
help NAME显示指定命令的帮助信息。
info break显示当前断点清单,包括到达断点处的次数等。
info files显示被调试文件的详细信息。
info func显示所有的函数名称。
info local显示当函数中的局部变量信息。
info prog显示被调试程序的执行状态。
info var显示所有的全局和静态变量名称。
kill终止正被调试的程序。
list显示源代码段。
make在不退出 gdb 的情况下运行 make 工具。
next在不单步执行进入其他函数的情况下,向前执行一行源代码。
print EXPR显示表达式 EXPR 的值。
print-object打印一个对象
print (int) name打印一个类型
set artist = @"test"设置变量值
what is查看变量的数据类型

[

](blog.csdn.net/likendsl/ar…)