在没有实现动态解析方法时,消息发送会进入最后一个流程:消息转发。我们可以实现对应的API方法进行最后的拦截,如果在最后一步消息转发还是没有实现,那么程序就会崩溃,我们可以看到如下异常
消息转发的底层实现,苹果并没有开放,我们可以在官方文档中学习他Objective-C Runtime Programming Guide
在实现之前,我们先了解消息转发的流程,我们熟悉了他的流程,然后看看每一步做了哪些事情。苹果在消息转发过程中给我们提供了6个API(实例方法三个,类方法三个)
消息转发我这里只实现了实例方法的,对象方法流程与实现是一样的。
代码实现
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([@"test:" isEqual:NSStringFromSelector(aSelector)]) {
return [[MLCar alloc] init];
}
return nil;
}
aSelector是需要消息转发的SEL
方法需要返回一个消息接收对象,如果是实例方法需要返回一个实例对象,class方法需要放回一个class对象,这样消息会重新走objc_msgSend,这是消息接受者就会变成这里renturn的对象。
- (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");
}
方法编码的封装