OC-Runtime消息转发

Runtime 是 Objective-C 区别于 C 语言这样的静态语言的一个非常重要的特性。对于 C 语言,函数的调用会在编译期就已经决定好,在编译完成后直接顺序执行。但是 OC 是一门动态语言,函数调用变成了消息发送,在编译期不能知道要调用哪个函数。所以 Runtime 无非就是去解决如何在运行时期找到调用方法这样的问题。

消息发送

消息发送

消息转发

当向一个对象发送一条消息,但它并没有实现的时候,_objc_msgForward会尝试做消息转发

image

简单打印下消息转发的log(Xcode 10及以上未出现打印)

首先随便创建个类(RuntimeTest 继承与NSObject,未实现test方法),编写一下代码

RuntimeTest *test = [[RuntimeTest alloc] init];
[test performSelector:@selector(test)];

开启debug模式,在下面这句加断点,设备选择模拟器

RuntimeTest *test = [[RuntimeTest alloc] init];

运行代码,当执行到断点暂停时在lldb中输入

po (void)instrumentObjcMessageSends(YES)

继续执行代码直至崩溃,使用终端 open /private/tmp/ 打开文件夹找到msgSends-xxxx(最新时间生成)文件,(文件部分内容)

- NSObject NSObject performSelector:
+ NSObject NSObject resolveInstanceMethod:
+ NSObject NSObject resolveInstanceMethod:
- NSObject NSObject forwardingTargetForSelector:
- NSObject NSObject forwardingTargetForSelector:
- NSObject NSObject methodSignatureForSelector:
- NSObject NSObject methodSignatureForSelector:
- NSObject NSObject class
- NSObject NSObject doesNotRecognizeSelector:
- NSObject NSObject doesNotRecognizeSelector:
- NSObject NSObject class

结合NSObject文档,可以知道_objc_msgForward消息转发做了如下几件事:

  1. 通过resolveInstanceMethod得知方法是否为动态添加,YES则通过class_addMethod动态添加方法,处理消息,否则进入下一步。dynamic属性就与这个过程有关,当一个属性声明为 dynamic时 就是告诉编译器:开发者一定会添加setter/getter的实现,而编译时不用自动生成。

  2. 这步会进入forwardingTargetForSelector用于指定哪个对象来响应消息。如果返回nil则进入第三步。这种方式把消息原封不动地转发给目标对象,有着比较高的效率。如果不能自己的类里面找到替代方法,可以重载这个方法,然后把消息转给其他的对象。

  3. 这步调用methodSignatureForSelector进行方法签名,这可以将函数的参数类型和返回值封装。如果返回nil说明消息无法处理并报错unrecognized selector sent to instance,如果返回methodSignature,则进入forwardInvocation,在这里可以修改实现方法,修改响应对象等,如果方法调用成功,则结束。如果依然不能正确响应消息,则报错unrecognized selector sent to instance.

使用场景:

  • 可以在消息转发过程中,利用第3步forwardInvocation使用try进行异常捕捉不抛出,从而避免一些崩溃
  • 可以利用 2、3 中的步骤实现对接受消息对象的转移,可以实现“多重继承”的效果。
  • 等等...

博客链接:博客链接

你可能感兴趣的:(OC-Runtime消息转发)