CC的博客

  • 首页
  • iOS
  • Android
  • React-Native
  • 读书杂谈
  • About
CC
记录美好生活
  1. 首页
  2. 技术编程
  3. iOS
  4. 正文

iOS消息转发机制

2020/08/26

经典错误再现

#import "ViewController.h"
#import "TestObject.h"
@interface ViewController ()

@property (nonatomic, strong) TestObject *testObj;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.testObj = [TestObject new];
    [self.testObj performSelector:@selector(testFunction)];
}


@end

调用了不存在的方法导致崩溃报错,报错信息如下:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[TestObject testFunction]: unrecognized selector sent to instance

消息转发机制

s

从全局来看,消息转发机制共分为3大步骤:
1. Method resolution 方法解析处理阶段
2. Fast forwarding 快速转发阶段
3. Normal forwarding 常规转发阶段

1、Method resolution 方法解析处理阶段

如果调用了对象方法会先调用resolveInstanceMethod函数进行判断。如果返回YES代表能接收消息;如果返回NO代表不能接收消息,则进入第二步处理。

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if ([NSStringFromSelector(sel) isEqualToString:@"testFunction"]) {
        return NO;
    }
    return [super resolveInstanceMethod:sel];
}

2、Fast forwarding 快速转发阶段

从第一步进来的代表自身不能响应消息,需要返回一个可以响应消息的实例对象(新建了BackupTestObject类,并声明和实现了testFunction方法)。
如果返回了实例对象,则转发SEL到此对象;如果返回nil,咋进入第三步。

- (id)forwardingTargetForSelector:(SEL)aSelector {
    if ([NSStringFromSelector(aSelector) isEqualToString:@"testFunction"]) {
        return [BackupTestObject new];
    }
    return [super forwardingTargetForSelector:aSelector];
}
3、Normal forwarding 常规转发阶段

如果第2步返回nil,则说明没有可以响应的实例对象,则进入第3步。
第3步的消息转发机制需要返回一个可以响应的对象,并手动将消息转发给响应对象。

实现步骤如下

一、对SEL消息进行签名

通过实现methodSignatureForSelector 返回SEL消息签名

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if ([super methodSignatureForSelector:aSelector] == nil) {
        BackupTestObject *backupTestObj = [BackupTestObject new];
        NSMethodSignature *sign = [backupTestObj methodSignatureForSelector:aSelector];
        return sign;
    }
    return [super methodSignatureForSelector:aSelector];
}

二、进行消息传递处理

如果上一步SEL消息签名不为空,则进入消息传递处理,把消息转发给可响应对象。

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    BackupTestObject *backupTestObj = [BackupTestObject new];
    SEL sel = anInvocation.selector;
    if ([backupTestObj respondsToSelector:sel]) {//消息可被响应
        [anInvocation invokeWithTarget:backupTestObj];
    } else {//无可响应对象,抛异常
        [self doesNotRecognizeSelector:sel];
    }
}

标签: 暂无
最后更新:2023/11/26

CC

这个人很懒,什么都没留下

点赞
下一篇 >

COPYRIGHT © 2020 CC的博客. ALL RIGHTS RESERVED.

Theme Kratos

豫ICP备2023032048号