发送消息时会在对应的类对象中搜索方法列表,如果找不到则会沿着继承树向上一直搜索知道继承树根部(通常为NSObject),如果还是找不到并且消息转发都失败了就回执行doesNotRecognizeSelector:
方法报unrecognized selector
错。
消息转发到底是什么呢?接下来将会逐一介绍最后的三次机会。
1、动态方法解析
2、备用接收者
3、完整消息转发
动态方法解析
首先,Objective-C运行时会调用+resolveInstanceMethod:
或者+resolveClassMethod:
,让你有机会提供一个函数实现。
如果你添加了函数并返回YES
, 那运行时系统就会重新启动一次消息发送的过程。
备用接收者
如果目标对象实现了-forwardingTargetForSelector:
,Runtime 这时就会调用这个方法,给你把这个消息转发给其他对象的机会。
完整消息转发
如果在上一步还不能处理未知消息,则唯一能做的就是启用完整的消息转发机制了。
首先它会发送-methodSignatureForSelector:
消息获得函数的参数和返回值类型。
如果-methodSignatureForSelector:
返回nil
,Runtime则会发出-doesNotRecognizeSelector:
消息,程序这时也就挂掉了。如果返回了一个函数签名,Runtime就会创建一个NSInvocation
对象并发送-forwardInvocation:
消息给目标对象。
签名参数"v@:"
示例:执行某个没有实现的方法时,会抛出异常并造成crash,但使用runtime运行时特性进行动态方法解析,或使用备用接收者,或进行完整的消息转发后,则可以很好的避免crash问题。
crash时的源码
#import "ViewController.h"
#import <objc/runtime.h>
#import <objc/message.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.title = @"runtime";
// 执行work函数,但work函数没有实现
[self performSelector:@selector(work:)];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
执行结果,抛出异常,造成crash
2018-07-05 18:41:18.160643+0800 DemoRuntime[9374:632785] -[ViewController work:]: unrecognized selector sent to instance 0x7f917a610f50
2018-07-05 18:41:18.209071+0800 DemoRuntime[9374:632785] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[ViewController work:]: unrecognized selector sent to instance 0x7f917a610f50'
*** First throw call stack:
(
0