上篇文章我们介绍了动态方法决议,了解了系统对于我们进行消息发送时查找IMP,在通过缓存查找以及方法列表查找后,如果没有找到IMP的话会进入动态方法决议流程,我们也分析了如果在动态方法决议过程中进行消息拦截的优缺点,同时也提到了一个新的东西消息转发,那么我们今天就开始探索下消息转发的流程。
消息转发
1、首先我们创建一个新的工程,然后创建一个KGTeacher类,继承于NSObject,然后在KGTeacher类中添加一个方法- (**void**)sayHello;,然后我们在main.m中添加如下代码:
2、然后这块的instrumentObjcMessageSends的是干嘛的呢?我们在objc源码中可以看到,如下代码:
3、然后在它的上方我们能够看到如下一块代码:
4、上述代码中也有详细的注释,意思就是当进行消息发送的时候,会创建一个/tmp/msgSends-%d的日志,然后我们运行代码,会发现系统在/tmp/文件夹下创建了一个日志如下:
5、然后打开日志后我们能够发现,里面详细记录了消息发送所调用的方法,如下图:
6、那么我们就此可以进行下一步的操作,也就是消息转发流程。
消息快速转发
对象方法消息快速转发
1、我们通过上述步骤可以看到,系统是经过动态方法决议后,也就是resolveInstanceMethod之后才开始的消息转发,然后第一个调用的是forwardingTargetForSelector,然后我们在objc源码中进行搜索看下系统的实现:
2、可以通过源码看到一个是+方法一个是-方法,对应的是类方法和对象方法。同时返回值是一个id类型的,因为我们是调用的对象方法,所以此处应该返回一个对象,所以我们自己进行实现,去给定一个对象,看看能否实现消息快速转发。
3、我们创建一个继承于NSObject的类KGStudent,然后在KGStudent类中实现- (**void**)sayHello,具体代码如下:
4、然后我们在KGTeacher类中进行消息快速转发,具体代码如下:
5、然后我们运行项目,看看具体效果是怎样的,能否实现消息的快速转发呢?结果如下:
类方法消息快速转发
1、我们上面步骤查看的时候,看到了forwardingTargetForSelector有个+方法,我们刚才使用的是-方法,那么我们是否可以猜测这个是用来处理类方法的消息快速转发的呢?下面我们在KGTeacher类中暴露一个类方法,然后不实现,然后在main.m中进行调用,具体如下:
2、然后我们运行代码,看下结果:
3、我们能够看到消息转发成功。因为上面我们看的时候,在快速转发流程后,又走了三个方法,我们继续看下后面的方法的作用以及用法。
慢速消息转发
对象方法消息慢速转发
1、首先我们看下methodSignatureForSelector这个函数的具体实现,通过在objc源码中搜索,我们能够看到如下代码:
2、但是具体怎么使用呢?既然它需要一个NSMethodSignature类型的,那么我们就返回一个NSMethodSignature类型的,但是应该怎么写呢?我们先看下系统给我提供的方法有那些,这个NSMethodSignature应该怎么创建,我们通过option+右键打开如下代码:
3、提供了一个对象方法,对象方法中需要传入const char*类型的参数,当看到此处的时候,我想很多人应该都能看出来,这个不就是Type Encodings嘛,我们之前也介绍过这块,所以我们只需要获取到方法的Type Encodings就可以了,我们可与通过method_getTypeEncoding来获取,但是我们需要得到一个Method,我们既然知道SEL那么我们就可以通过class_getInstanceMethod获取,因为我们调用的是对象方法。具体代码如下:
4、然后我们直接运行程序,发现程序还是奔溃,还是报错方法找不到实现,那么问题出在哪里呢?当我查看官方文档的时候,看到有个forwardInvocation的解释,而且我们打印里面也有这个,那是否是这个问题呢?我们尝试实现这个方法,因为这个方法有个参数anInvocation,是个NSInvocation类型的,然后我们看下这个类的实现,具体如下:
5、那么我们就找到问题了,需要一个target去实现方法,所以我们修改下代码,最终结果如下:
6、然后我们再次运行代码,看下结果:
类方法消息慢速转发
1、同样的我们进行对类方法的消息慢速转发,我们同样实现methodSignatureForSelector这个函数,然后使用class_getClassMethod来获取Method因为我们对类方法进行了消息转发。具体代码如下:
2、然后我们运行代码,看下效果:
3、消息转发成功了,那就是说明这个慢速转发我们已经完美实现了,具体在开发过程中怎么使用,需要结合实际业务去拓展了。
4、最后我们进行全局搜索doesNotRecognizeSelector这个方法的实现的时候,看到如下的内容:
5、所以这个方法就是消息转发最终的走向,如果经过多次补救还是没有找到的话,那么就直接进入这个方法,打印提示信息。
总结
- 消息转发流程图