前言
上一篇我们探索了动态方法决议,那么如果我们没有在动态发方法决议中处理未实现的方法,还有其他的方法么?带着疑问我们继续分析源码,首先在找到方法时候会进行log_and_fill_cache
,我们进入到这个方法中查看,如图:
这里主要有2个方法
logMessageSend
和insert
,insert
在前面的说过,是插入方法。那么这个logMessageSend
是做什么的呢?我们再次进入,如图:
在这个方法中注释这
create/open the log file
创建或者打开日志文件,且objcMsgLogFD
的值为-1
,所以这个方法必然会打开日志文件。但是objcMsgLogEnabled = false
,全局搜索objcMsgLogEnabled
,结果如下:
浏览之后发现只有在instrumentObjcMessageSends
会对objcMsgLogEnabled
赋值false
以外的值,如图:
所以我们对instrumentObjcMessageSends
进行拓展,然后在外部就可以控制objcMsgLogEnabled
的值。如图:
再次运行程序,崩溃,然后前往文件夹tmp/msgSends
(logMessageSend
创建的目录),成功地找到了相关文件,打开如图:
消息转发
我们通过logMessageSend
生成的文件发现在2次resolveInstanceMethod
中间插入了forwardingTargetForSelector
和methodSignatureForSelector
这2个方法时干什么的?我们不得而知。全局搜索也只能看到一些声明和空实现,如图:
如何查找呢?打开Developer Documentation
,如图所示:
快捷键shift + command + 0
,打开后搜索forwardingTargetForSelector
和methodSignatureForSelector
。
1、forwardingTargetForSelector
forwardingTargetForSelector
的搜索结果如下:
返回一个接收未识别消息的对象,简单的说就是我没有实现这条消息,但是我可以通过这个方法指定一个实例对象来处理这条消息(甩锅哦!)。我们来尝试一下,如图:
在forwardingTargetForSelector
中返回LGPerson
的一个实例对象对象,然后再运行,如图:
调用
Person
实例的sayWork
,没崩溃,而且打印出来 -[LGPerson sayWork]
,说明我们成功的把锅甩给了LGPerson
。这个种甩锅的过程叫做快速转发
2、methodSignatureForSelector
methodSignatureForSelector
的搜索结果:
这里好像没说具体的功能,往下翻:
大概意思:返回一个NSMethodSignature
对象,该对象包含由给定选择器标识的方法的描述。
再点击- methodSignatureForSelector:
如图:
大概意思是 返回一个NSMethodSignature
对象,该对象包含由给定选择器标识的方法的描述。
我们尝试写一下这个方法:
运行程序,发现还是崩了,如图:
但是走了
methodSignatureForSelector
,那如何处理才能不崩溃?我们继续看描述:
这里有个相关的方法forwardInvocation
,描述如下:
我们看到这个 Important
的内容,大概意思就是重写这个方法必须先重写methodSignatureForSelector
,我们已经重写了,所以写入这个方法试试,如图:
然后运行程序,如图:
我们清楚的看到sayWork
在forwardInvocation
中打印出来,我们又一次处理了未实现方法的崩溃处理。这种处理消息imp
找不到的方法叫做慢速查找。
总结
结合前面2片文章runtime和方法的本质和动态方法决议,完整的消息查找流程就完善了。