原文链接 : 原文 假设说我们声明一个类, 初始化对象, 并且在此类声明一个方法, 调用方法的时候底层是怎么处理的呢? 今天我们来简单模拟测试, 来看道理发生了什么 以下是调用方法处理的方案图, 按照方案顺序去处理
以下是测试方法
- //消息转发
- //- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
- //标准的消息转发
- //- (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");
- //- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
- //
- //动态方法解析
- //+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
- //+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
创建类, Person 类, 声明方法, 并且在 ViewController 进行初始化调用
- VC中
- Person *person = [Person new];
- [person run];
- Person类中
- - (void)run;//没有实现
此时运行是不是会报错呢? 就是这个常见的错误 "- [Person run]: unrecognized selector sent to instance 0x600000008310' " 那么这样做到底发生了什么? 做了哪些事情? 我们一步步来剖析
在 Presenter 类中, 写动态方法
- + (BOOL) resolveInstanceMethod: (SEL) sel {
- NSLog(@"sel = %@", NSStringFromSelector(sel));
- return [super resolveInstanceMethod: sel];
- }
再次运行 Demo 就会走到这个方法中, 也就是我们所指的方案 1, 此时打印出来的 scl 为 "消息转发机制 Demo[41829:4186268] sel = run "
- + (BOOL) resolveInstanceMethod: (SEL) sel {
- NSLog(@"sel = %@", NSStringFromSelector(sel));
- //1.判断没有实现方法, 那么我们就是动态添加一个方法
- if (sel == @selector(run: )) {
- class_addMethod(self, sel, (IMP) newRun, "v@:@:");
- return YES;
- }
- return [super resolveInstanceMethod: sel];
- }
声明函数
- void newRun(id self, SEL sel, NSString * str) {
- NSLog(@"---runok---%@", str);
- }
温馨小提示, 动态添加方法参数意译 : // 将要添加方法的类 / sel 名 / IMP 函数指针 <添加函数>, 官方文档其实是有解释的
此时我们再次运行, 那么打印结果就来了 "消息转发机制 Demo[43269:4212899] ---runok---ok 跑 ", 这样的话我们就解决掉了报错这个问题
此时我们新创建一个类 Mbxb, 此时我们还是重新写一个同名字的方法 run 方法, 并且进行实现
- - (void) run {
- NSLog(@"---Mbxbrunok---");
- }
此时有两个同样的方法, 我们重新在 Person 类中 来实现方法
- - (id) forwardingTargetForSelector: (SEL) aSelector {
- NSLog(@"aSelector = %@", NSStringFromSelector(aSelector));
- return [super forwardingTargetForSelector: aSelector];
- }
此时运行测试, 动态测试输出 "消息转发机制 Demo[45875:4255869] sel = run ", 消息转发重定向输出" 消息转发机制 Demo[45875:4255869] ---Mbxbrunok--- ", 同样也可以找见方法 run 当我们进行处理
- - (id) forwardingTargetForSelector: (SEL) aSelector {
- NSLog(@"aSelector = %@", NSStringFromSelector(aSelector));
- return [[Mbxb alloc] init];
- }
那么此时运行成功输出, "---Mbxbrunok---"
此时我们在 Person 类中, 生成方法签名
- - (NSMethodSignature * ) methodSignatureForSelector: (SEL) aSelector OBJC_SWIFT_UNAVAILABLE("") {
- //转化字符
- NSString * sel = NSStringFromSelector(aSelector);
- //判断, 手动生成签名
- if ([sel isEqualToString: @"run"]) {
- return [NSMethodSignature signatureWithObjCTypes: "v@:"];
- } else {
- return [super methodSignatureForSelector: aSelector];
- }
拿到签名
- - (void) forwardInvocation: (NSInvocation * ) anInvocation OBJC_SWIFT_UNAVAILABLE("") {
- NSLog(@"---%@---", anInvocation);
- return [super forwardInvocation: anInvocation];
- }
此时我们的 po 的签名输出为 "<NSInvocation: 0x60400027e700> return value: {v} void target: {@} 0x600000016ba0 selector: {:} run "
拿到消息转发签名
- - (void) forwardInvocation: (NSInvocation * ) anInvocation OBJC_SWIFT_UNAVAILABLE("") {
- NSLog(@"---%@---", anInvocation);
- //取到消息
- SEL seletor = [anInvocation selector];
- //转发
- Mbxb * bxb = [[Mbxb alloc] init];
- if ([bxb respondsToSelector: seletor]) {
- //调用对象,进行转发
- [anInvocation invokeWithTarget: bxb];
- } else {
- return [super forwardInvocation: anInvocation];
- }
- }
假如说我们没有这个方法, 同样是遇到会崩溃的问题 我们这里来进行一个异常处理
- - (void) doesNotRecognizeSelector: (SEL) aSelector {
- NSString * selStr = NSStringFromSelector(aSelector);
- NSLog(@"%@不存在", selStr);
- }
我们可以在这个异常处理中一些处理, 比如说弹框
对于消息转发机制, 我们重新来梳理一下 Demo 解析思路 还是三个方案, 按顺序来走
最后献上一张逻辑图
好了, 给大家这个简单 demo, 当然在代码中也写了注释, 可以去我的 git 下载, 欢迎 star 下载链接 : demo 地址
技术交流 q 群 150731459
来源: https://juejin.im/post/5a332ecdf265da4309453320