ios-对象的原理探索十-动态方法决议&消息转发流程

649 阅读3分钟

ios-对象的原理探索九-消息查找

前言:

我们开发过程中调用方法,有时会遇到这样的错误

因为该反复为实现导致的崩溃,那么从上篇文章中我们得到了一个这样的结论,当方法imp未找到时lookUpImpOrForwrd会返回

那么这篇文章就着重分析下resolveMethod_locked的作用.

resolveMethod_locked源码分析

流程如下:

resolveInstanceMethod 实例方法决议

上个流程图可以看出当前类非元类的时候走的是resolveInstanceMethod方法决议,进入

实例方法决议

resolveInstanceMethod源码分析

因为say666是实例方法,通过上图的源码分析,苹果爸爸又执行lookUpImpOrNil方法,即通过lookUpImpOrNil方法又会进入lookUpImpOrForward慢速查找流程找resolveInstanceMethod方法如果未找到返回,找到返回imp 所以,针对实例方法say666未实现的报错崩溃,可以通过在重写resolveInstanceMethod类方法,并将其指向其他方法的实现

为什么会打印两次,查看堆栈信息具体分钟,在控制台输入bt

继续断点跟进

通过两次的打印流程,为什么会走CoreFoundation__methodDescriptionForSelector‘`后才进行方法决议?

类方法决议

通过堆栈信息可以看出类方法决议的过程也是两次的调用?

从上图的流程看,在崩溃之前均需要走**resolveInstanceMethod是否意味着可以在此位置修复崩溃级bug,答案是当然可以,根据代码和isa走位图分析,实例方法resolveInstanceMethod** 方法走的是**类 -- 父类 -- 根类 -- nil流程最终调用的是NSObject的****resolveInstanceMethod方法,类方法resolveClassMethod走的是元类 -- 根元类 -- 根类 -- nil流程.最终也是调用NSObject``的resolveInstanceMethod方法,那么就可以复写NSObject的resolveInstanceMethod的方法实现崩溃拦截,**添加NSObject添加分类重写resolveInstanceMethod方法具体如下:

消息转发流程

上一个小结分析了消息在为查找到imp的状态下进行了方法的决议,那么在程序崩溃之前是否还有其他的方法可以调用,继续lookUpImpOrForward断点跟进

根据代码流程获取日志

main函数中实现instrumentObjcMessageSends方法,在tmp中获取日志,具体如下:

再访达中前往文件地址查看msgSends文件

有此文件可以看出在崩溃之前我们可以

  • 两次动态方法决议resolveInstanceMethod方法

  • 两次消息快速转发forwardingTargetForSelector方法

  • 两次消息慢速转发methodSignatureForSelector + resolveInstanceMethod

由此可见,苹果爸爸在给我们崩溃的时候提供了三次挽救崩溃的机会,那么我们就会得出一张经典的消息转发流程图,如下:

forwardingTargetForSelector

在源码中未开放此方法的实现,只能通过汇编反编译去了解,也可以通过api文档去了解

如果返回nil,可以通过消息的慢速转发流程处理,具体如下:

methodSignatureForSelector/forwardInvocation

具体文档如下:

最后的崩溃

如果你没有实现第三步那么程序就真的要走崩溃流程了
- doesNotRecognizeSelector:然后闪退。

以上就是消息的转发流程,在文章中有个问题没有解决,就是为什么会有两次的方法调用...具体的原因待下次分析.