runtime之消息转发机制

在程序中,调用我们未实现或找不到的方法时,程序就会崩溃,如下

Person *person = [Person new];

//    [person run];

[person performSelector:@selector(run)];

调用person类的run方法时,会报下面错


runtime之消息转发机制_第1张图片
图1.png

其实,抛异常前,这个消息经过了三件事,我们重写这几个方法,就能很好的改变这些方法的实现,实现消息转发

1、动态添加方法:尝试动态给目标类(比如这个例子为Person)添加一个方法来实现这个未知的selector。

2、消息转发重定向:尝试寻找另一个能够识别该selector的receiver。

3、标准的消息转发:对消息使用NSInvocation对象进行重新包装,给receiver最后一次识别机会。

示意图如下

runtime之消息转发机制_第2张图片
图2.png

一、动态添加方法 resolveInstanceMethod

我们这里用newRun这个方法替换这个未实现的run

+ (BOOL)resolveInstanceMethod:(SEL)sel {

    NSLog(@"sel = %@", NSStringFromSelector(sel));

    // 1、动态添加方法

    if (sel == @selector(run)) {

        class_addMethod(self, sel, (IMP)newRun, "v@:v");

        return YES;

    }

    return [super resolveInstanceMethod:sel];

}

newRun方法

void newRun(id self,SEL sel, NSString *str) {

    NSLog(@"--------run is implement %@--------", str);

}

二、 消息重定向forwardingTarget

这里将Person的run方法重定向到Animation的run实现

- (id)forwardingTargetForSelector:(SEL)aSelector {

    NSLog(@"%@",NSStringFromSelector(aSelector));

    // 2、消息转发重定向

    if ([NSStringFromSelector(aSelector) isEqualToString:@"run"]) {

        return [Animation new];

    } else {

        return [super forwardingTargetForSelector:aSelector];

    }

}

三、标准的消息转发 forwardInvocation

  // 3、生成方法签名

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

    NSString *sel = NSStringFromSelector(aSelector);

    if ([sel isEqualToString:@"run"]) {

        return [NSMethodSignature signatureWithObjCTypes:"v@:"];

    } else {

        return [super methodSignatureForSelector:aSelector];

    }

}

// 4、拿到方法签名配发消息

- (void)forwardInvocation:(NSInvocation *)anInvocation {

    NSLog(@"-----%@",anInvocation);

    SEL seletor = [anInvocation selector];

    Animation *anim = [Animation new];

    if ([anim respondsToSelector:seletor]) {

        [anInvocation invokeWithTarget:anim];

    } else {

        [super forwardInvocation:anInvocation];

    }

}

最后,如果跑了这几个方法都没有找到,我么还可以给一个友好的提示

// 5、抛出友好异常
 - (void)doesNotRecognizeSelector:(SEL)aSelector {
     NSString *selStr = NSStringFromSelector(aSelector);
     NSLog(@"%@ dose not recognize.......",selStr);
 }

你可能感兴趣的:(runtime之消息转发机制)