OC-RunTime: 消息转发之实例方法的转发流程 分享了消息转发的流程, 本次结合实际例子继续分析一下消息转发流程.
发送不存在的消息
在 ViewController 的 viewDidLoad 中运行 veryTestMethod
方法.
ViewController.m
1 | static NSString * const sPerformInstanceMethodName = @"veryTestMethod"; |
其中, SuppressPerformSelectorLeakWarning
是定义的一个宏.
1 |
在 ViewController 中我并没有写 veryTestMethod
这个函数, 只是借助 performSelector
动态执行, 如果编译运行直接会 crash.
可以查看 NSObject.mm
源码, 里面关于消息转发的几个重要函数都写着 _objc_fatal
, 可谓是招招毙命.
紧接着, 我们可以借助 resolveInstanceMethod
来完成消息转发给 ViewController.
resolveInstanceMethod 转发
重写 NSObject 中的 resolveInstanceMethod
函数.
+resolveInstanceMethod
ViewController.m
1 | // 记得导入 RunTime 头文件 |
ViewController 中实现的 proxyMethod
1 | // OC 实现 |
在 resolveInstanceMethod
中动态添加了 veryTestMethod
方法, 并让 proxyMethod
函数来实现(IMP).
运行可以看到, 程序并没有 crash, 成功的执行了 proxyMethod
.
1 | ---veryitman--- 1--- +resolveInstanceMethod |
到此为止, 我们已经看到动态添加一个方法的实现并成功运行的完整例子.
接下来, 我们将转发给其他对象 MZTempObj
来执行.
自定义被转发的对象
MZTempObj.m
1 | @implementation MZTempObj |
veryTestMethod
就是我们要转发对应的消息.
消息转发实践
接下来我们把向 ViewController 发送 veryTestMethod
的消息转发给 MZTempObj
的 veryTestMethod
方法.
继续重写下面函数, 不过 resolveInstanceMethod
要稍微改造一下, 才能达到我们实践的目的.
- +resolveInstanceMethod
- -forwardingTargetForSelector
- -methodSignatureForSelector
- -forwardInvocation
- -doesNotRecognizeSelector:
ViewController.m
1 | // 记得导入 RunTime 头文件 |
运行程序, 控制台打印结果如下:
1 | ---veryitman--- 1--- +resolveInstanceMethod |
这里对照之前的流程图是完全符合的, 那么怎么让其执行 3 和 4 呢? 很简单, 修改一下 forwardingTargetForSelector
里面的实现即可.
1 | - (id)forwardingTargetForSelector:(SEL)aSelector |
再次执行看结果:
1 | ---veryitman--- 1--- +resolveInstanceMethod veryTestMethod |
注意: 这里在 3后面会多了一个 1--- resolveInstanceMethod
的打印, 是系统调用的, 此时对应的 sel 是 _forwardStackInvocation
.
如果不去重写 methodSignatureForSelector
打印结果如下:
1 | ---veryitman--- 1--- +resolveInstanceMethod |
参考文档
1.Apple RunTime 源码 objc4-723.tar.gz
推荐
点击下载文中完整的 Demo.
扫码关注,你我就各多一个朋友~