Runtime消息转发机制

来源:互联网 发布:智能音箱 知乎 编辑:程序博客网 时间:2024/05/16 15:11

@interface ViewController ()@property (nonatomic, copy) NSString *name;@property (nonatomic, copy) NSString *age;@end@implementation ViewController// 告诉编译器,不要生成set 和 get方法,但是调用get 和set也不会出错@dynamic name;- (void)viewDidLoad {    [super viewDidLoad];    // Do any additional setup after loading the view, typically from a nib.    }

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{    self.name = @"pangshishan";}

上述代码中中name是一个属性,在@implementation中,用@dynamic修饰之后,name属性将不会生成getter 和 setter方法。在下面这段代码中,用setter方法给name复制,系统会抛出“ -[ViewController setName:]: unrecognized selector sent to instance 0x7f95f2f07100”异常,这是由于系统找不到setName:这个方法。用runtime消息转发,有三次机会可以不让系统抛出这个异常(当然,把这个方法写上肯定是没问题的,但我们今天主题是:Runtime的转发机制)


我们在SomeOtherObject的 .m中实现setName:方法,让SomeOtherObject接受未处理的消息


#pragma mark - 1.重定向方法,在本类里进行方法重定向处理void dynamicMethodIMP(id self, SEL _cmd){    NSLog(@"dynamicMethodIMP -- %@", NSStringFromSelector(_cmd));}+ (BOOL)resolveInstanceMethod:(SEL)sel{    NSLog(@"sel is %@", NSStringFromSelector(sel));    // 当方法是setter方法时    if (sel == @selector(setName:)) {        // 动态给本类添加方法        class_addMethod([SomeOtherObject class], sel, (IMP)dynamicMethodIMP, "v@:");    }    return [super resolveInstanceMethod:sel];}#pragma mark - 2. 找个类处理她- (id)forwardingTargetForSelector:(SEL)aSelector{    if ([NSStringFromSelector(aSelector) isEqualToString:@"setName:"]) {        return [[SomeOtherObject alloc] init];    }    return [super forwardingTargetForSelector:aSelector];}#pragma mark - 3. 最后一步,把selector的所有细节包程NSInvocation, invocation对象负责分发消息给其他对象,这一步是完整的转发机制- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{    // (1)把2注释掉,1解开注释,依然有效//    if ([NSStringFromSelector(aSelector) isEqualToString:@"setName:"]) {//        return [NSMethodSignature signatureWithObjCTypes:"v@:"];//    }//    return [super methodSignatureForSelector:aSelector];    // (2)    NSMethodSignature *signature = [super methodSignatureForSelector: aSelector];    if (signature == nil) {        if ([SomeOtherObject instancesRespondToSelector:aSelector]) {            signature = [SomeOtherObject instanceMethodSignatureForSelector:aSelector];        }    }    return signature; // 只有当返回值不是空的时候才能转发成功}// anInvocation里打包了此类不能处理的selector所有的细节- (void)forwardInvocation:(NSInvocation *)anInvocation{    // 将anInvocation里打包的消息,发送给SomeOtherObject处理,如果SomeOtherObject也不能处理,可以在它里面继续转发    // 这也是变相的实现了多继承,但是消息转发只是将一部分功能放到其他类,并没有多继承造成的代码过于丰富的问题    [anInvocation invokeWithTarget:[[SomeOtherObject alloc] init]];}

以上简单的介绍了一下Runtime的消息转发机制,这是我写的demo,感兴趣的小伙伴可以下载

点击这里查看demo

0 0