在没有实现动态解析方法时,消息发送会进入最后一个流程:消息转发。我们可以实现对应的API方法进行最后的拦截,如果在最后一步消息转发还是没有实现,那么程序就会崩溃,我们可以看到如下异常

Untitled

消息转发的底层实现,苹果并没有开放,我们可以在官方文档中学习他Objective-C Runtime Programming Guide

消息转发的流程

在实现之前,我们先了解消息转发的流程,我们熟悉了他的流程,然后看看每一步做了哪些事情。苹果在消息转发过程中给我们提供了6个API(实例方法三个,类方法三个)

  1. 系统会先调用-/+forwardingTargetForSelector:,如果有返回值,就重新走objc_msgSend流程;如果返回nil,那么就会走到第2步。
  2. 在第1步没有返回nil或者没有实现会走-/+methodSignatureForSelector:这个方法,有返回值会走到第3步,返回nil调用doesNotRecognizeSelector:方法,程序崩溃
  3. -/+forwardInvocation:我们可以在这个方法中做任何事情

Untitled

消息转发的实现

消息转发我这里只实现了实例方法的,对象方法流程与实现是一样的。

-/+forwardingTargetForSelector:

代码实现

- (id)forwardingTargetForSelector:(SEL)aSelector {
    if ([@"test:" isEqual:NSStringFromSelector(aSelector)]) {
        return [[MLCar alloc] init];
    }
    return nil;
}

aSelector是需要消息转发的SEL

方法需要返回一个消息接收对象,如果是实例方法需要返回一个实例对象,class方法需要放回一个class对象,这样消息会重新走objc_msgSend,这是消息接受者就会变成这里renturn的对象。

-/+methodSignatureForSelector:与forwardInvocation:

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSMethodSignature* signature = [super methodSignatureForSelector:aSelector];
    if (!signature) {
        signature = [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    }
    return signature;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    void *arg0 = nil;
    void *arg1 = nil;
    void *arg2 = nil;
    [anInvocation getArgument:&arg0 atIndex:0];
    [anInvocation getArgument:&arg1 atIndex:1];
    [anInvocation getArgument:&arg2 atIndex:2];
    NSLog(@"arg0=%@, arg1=%s, arg2=%@", (__bridge MLDog *)arg0, arg1, arg2);
    
    // 改变参数值
    NSNumber *num = @10;
    [anInvocation setArgument:&num atIndex:2];
    [anInvocation getArgument:&arg2 atIndex:2];
    NSLog(@"arg0=%@, arg1=%s, arg2=%@", (__bridge MLDog *)arg0, arg1, arg2);
    
    /// 改变调用者
    anInvocation.target = [[MLCar alloc] init];
    [anInvocation invoke];
    NSLog(@"111");
}

方法编码的封装