概况
当一个方法沿着类的继承链找不到实现时候,runtime会启动消息转发。消息转发一共有三步。
消息转发三步
- resolveInstanceMethod
#import "Cat.h"
#import <objc/runtime.h>
@implementation Cat
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if ([NSStringFromSelector(sel) isEqualToString:@"say"]) {
class_addMethod(self, sel,(IMP)test , "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void test(id self,SEL cmd){
NSLog(@"猫叫...");
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Cat *aCat = [[Cat alloc] init];
[aCat say];
}
@end
在调用对象的所属类的实现文件新增resolveInstanceMethod方法。判断选择子是不是我们要进行转发的,如果不是返回super方法。如果是就新增方法的实现,并返回YES消息转发结束。
- forwardingTargetForSelector
#import "Dog.h"
#import "Cat.h"
#import <objc/runtime.h>
@implementation Dog
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if ([NSStringFromSelector(sel) isEqualToString:@"say"]) {
return NO;
}
return [super resolveInstanceMethod:sel];
}
- (id)forwardingTargetForSelector:(SEL)aSelector{
return [Cat new];
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Dog *aDog = [[Dog alloc] init];
[aDog say];
}
@end
在调用所属类的实现文件里新增resolveInstanceMethod方法如果是当前选择子返回NO好进行消息转发第二步。接下来新增方法forwardingTargetForSelector返回cat对象。之后runtime系统会去cat类里去调用say的实现。
- forwardInvocation
#import "Dog.h"
#import "Cat.h"
#import <objc/runtime.h>
@implementation Dog
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if ([NSStringFromSelector(sel) isEqualToString:@"say"]) {
return NO;
}
return [super resolveInstanceMethod:sel];
}
- (id)forwardingTargetForSelector:(SEL)aSelector{
return nil;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
SEL aSel = anInvocation.selector;
Cat *aCate = [Cat new];
if ([aCate respondsToSelector:aSel]) {
[anInvocation invokeWithTarget:aCate];
}
}
@end
在调用类的实现文件里,新增resolveInstanceMethod方法返回NO,进入消息转发第二步。新增forwardingTargetForSelector方法返回nil,进入消息转发第三步。新增方法methodSignatureForSelector,forwardInvocation实现完整的消息转发。