Objective-C消息转发机制

2020-06-08  本文已影响0人  楼上那只猫

一段摘自官网的解释

Sending a message to an object that does not handle that message is an error. However, before announcing the error, the runtime system gives the receiving object a second chance to handle the message.

简单来说,就是当向一个对象发送它不能响应的消息时,会触发一系列动作,这个动作就是oc中的消息响应机制。

消息转发流程.png

大体来看,消息转发分3个阶段

  1. Method resolution 方法解析处理阶段
  2. Fast forwarding 快速转发阶段
  3. Normal forwarding 常规转发阶段

第一阶段:动态方法解析

在这一阶段,实际上还未进入消息转发阶段,在这个阶段系统为我们提供了一个动态为对象添加方法实现的机会。
主要是两个方法。

+ (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);

当发现对象不能识别发送的消息时,可以在对应类里实现对应方法。
如下:ViewController中有一个run方法。

@interface ViewController : UIViewController

- (void)run:(NSString *)meter;

@end

.m的viewDidLoad中调用。

- (void)viewDidLoad {
    [super viewDidLoad];
    [self run:@"fasf"];
    // Do any additional setup after loading the view.
}

因为并未实现该方法,所以在运行时会发生崩溃。

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[ViewController run:]: unrecognized selector sent to instance 0x7fa948f0aac0'

为了避免crash,我们可以手动实现该方法,未该类动态添加一个方法,当收到不能识别的run消息时,转而调用我们动态添加的方法。

// oc中方法默认有两个参数 self, _cmd
void tempRun(id self, SEL _cmd, id obj) {
    NSLog(@"%@", obj);
}

- (void)viewDidLoad {
    [super viewDidLoad];
    [self run:@"fasf"];
    // Do any additional setup after loading the view.
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    // 先判断不能识别的消息是哪个
    if (sel == @selector(run:)) {
        // 动态添加一个名字为tempRun的方法,参数为id
        // "v@:@":v表示返回值为void, @表示第一个参数为id类型, :表示第二个参数为A method selector (SEL),@表示第三个参数为id类型
        class_addMethod([self class], sel, (IMP)tempRun, "v@:@");
        return YES;
    }
    return NO;
}

对于动态添加的方法的参数类型表示,可以参考下图:


参数类型.png

第二阶段:快速转发

如果在第一阶段并没有实现动态添加方法,那么就会进入快速转发阶段,在这个阶段,可以快速的将该对象不能识别的消息转发给另一个实现了该消息的对象。

- (id)forwardingTargetForSelector:(SEL)aSelector;

如下:新增一个Person类,实现run方法

@interface Person : NSObject
- (void)run:(NSString *)meter;
@end

#import "Person.h"

@implementation Person
- (void)run:(NSString *)meter {
    NSLog(@"---------");
}
@end

ViewController中实现下面的方法.

- (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(run:)) {
        //将消息转发给Person类
        return [Person new];
    }
    return [super forwardingTargetForSelector:aSelector];
}

第三阶段:Normal forwarding 常规转发阶段

这一步的消息转发机制和第二步类似,只是更复杂一些,需要手动将要响应的消息转发给需要响应的对象。

主要分两步:

  1. 根据要转发的selector,生成对应的方法签名。
  2. 生成NSInvocation,将消息转发给需要响应的对象。

This method is used in the implementation of protocols. This method is also used in situations where an NSInvocation object must be created, such as during message forwarding. If your object maintains a delegate or is capable of handling messages that it does not directly implement, you should override this method to return an appropriate method signature

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;

在返回方法签名的实现中,有两种方式,一种是手动生成签名,一种是自动。

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSMethodSignature * signature = [super methodSignatureForSelector:aSelector];
    if (!signature) {
        Person *p = [Person new];
        //调用p的methodSignatureForSelector方法实现自动签名
        signature = [p methodSignatureForSelector:aSelector];
        
        //手动生成方法对应的签名
        //signature = [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    }
    return signature;
}

方法签名实现后,接下来就是最后一步的转发。

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    Person * p = [Person new];
    if ([p respondsToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:p];
    }else{
        [super forwardInvocation:anInvocation];
    }
}
- (void)forwardInvocation:(NSInvocation *)anInvocation;

When an object is sent a message for which it has no corresponding method, the runtime system gives the receiver an opportunity to delegate the message to another receiver. It delegates the message by creating an NSInvocation object representing the message and sending the receiver a forwardInvocation: message containing this NSInvocation object as the argument. The receiver’s forwardInvocation: method can then choose to forward the message to another object. (If that object can’t respond to the message either, it too will be given a chance to forward it.)

The forwardInvocation: message thus allows an object to establish relationships with other objects that will, for certain messages, act on its behalf. The forwarding object is, in a sense, able to “inherit” some of the characteristics of the object it forwards the message to

上一篇下一篇

猜你喜欢

热点阅读