在程序中,调用我们未实现或找不到的方法时,程序就会崩溃,如下
Person *person = [Person new];
// [person run];
[person performSelector:@selector(run)];
调用person类的run方法时,会报下面错
其实,抛异常前,这个消息经过了三件事,我们重写这几个方法,就能很好的改变这些方法的实现,实现消息转发
1、动态添加方法:尝试动态给目标类(比如这个例子为Person)添加一个方法来实现这个未知的selector。
2、消息转发重定向:尝试寻找另一个能够识别该selector的receiver。
3、标准的消息转发:对消息使用NSInvocation对象进行重新包装,给receiver最后一次识别机会。
示意图如下
一、动态添加方法 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);
}