/**
* 首先,方法在调用时,系统会查看这个对象能否接收这个消息(查看这个类有没有这个方法,或者有没有实现这个方法。),如果不能并且只在不能的情况下,就会调用下面这几个方法,给你“补救”的机会,你可以先理解为几套防止程序crash的备选方案,我们就是利用这几个方案进行消息转发,注意一点,前一套方案实现后一套方法就不会执行。如果这几套方案你都没有做处理,那么程序就会报错crash。
方案一:
+ (BOOL)resolveInstanceMethod:(SEL)sel。// 处理实例方法
+ (BOOL)resolveClassMethod:(SEL)sel。 //处理类方法
方案二:
- (id)forwardingTargetForSelector:(SEL)aSelector
方案三:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
- (void)forwardInvocation:(NSInvocation *)anInvocation;
*/
现在在一个类里面写下如下代码,但是这个类并没有 test4 这个方法的实现,然后就报错了
[self performSelector:@selector(test4) withObject:nil];
[ViewController test4]: unrecognized selector sent to instance 0x7fbab94098a0'
现在我们来用方案一来解决
在当前类里面添加代码
void dynamicMethodIMP(id self, SEL _cmd) {
NSLog(@" >> dynamicMethodIMP");
}
+(BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(test4)) {
class_addMethod([self class], sel, (IMP)dynamicMethodIMP, "v@:");
return YES;
}
return NO;
}
打印结果:
2017-03-07 16:54:13.815 runTime[88709:10317153] >> dynamicMethodIMP
- (id)forwardingTargetForSelector:(SEL)aSelector {
return [FirstViewController new];
}
其中FirstViewController 里面有实现方法
- (void)test4 {
NSLog(@"FirstViewController carryout test4");
}
然后打印结果就是
2017-03-07 16:56:38.385 runTime[89099:10319537] FirstViewController carryout test4
方案二 就是转移了实现方法的类 ,返回一个类来代替本类实现不了的方法
接下来是方案三
/**
* 方案三
开头我们要找的错误unrecognized selector sent to instance原因,原来就是因为methodSignatureForSelector这个方法中,由于没有找到run对应的实现方法,所以返回了一个空的方法签名,最终导致程序报错崩溃。
所以我们需要做的是自己新建方法签名,再在forwardInvocation中用你要转发的那个对象调用这个对应的签名,这样也实现了消息转发。
*/
/**
* methodSignatureForSelector
* 用来生成方法签名, 这个签名就是给forwardInvocation中参数NSInvocation调用的
*
*/
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSString *sel = NSStringFromSelector(aSelector);
if ([sel isEqualToString:@"test4"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
-(void)forwardInvocation:(NSInvocation *)anInvocation
{
SEL selector = [anInvocation selector];
// 新建需要转发消息的对象
FirstViewController *car = [[FirstViewController alloc] init];
if ([car respondsToSelector:selector]) {
// 唤醒这个方法
[anInvocation invokeWithTarget:car];
}
}
2017-03-07 17:09:51.908 runTime[91235:10334599] FirstViewController carryout test4
/**
* 关于生成签名类型"v@:"解释一下, 每个方法会默认隐藏两个参数, self, _cmd
self 代表方法调用者, _cmd 代表这个方法SEL, 签名类型就是用来描述这个方法的返回值, 参数的,
v代表返回值为void, @表示self, :表示_cmd
*/