unrecognized selector sent to instance 0x10010c840
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Foo MissMethod]: unrecognized selector sent to instance 0x10010c840'
*** Call stack at first throw:
......
terminate called after throwing an instance of 'NSException'
因为app里经常会有方法未找到unrecognized selector 报crash,所以会加一些保险。
针对这个问题 可以有三个方向可加
首先Objective-C在运行时调用+ resolveInstanceMethod:或+ resolveClassMethod:方法,让你添加方法的实现。如果你添加方法并返回YES,那系统在运行时就会重新启动一次消息发送的过程
举一个简单例子,定义一个类Message,它主要定义一个方法sendMessage,下面就是它的设计与实现:
@implementation Message
- (void)sendMessage:(NSString *)word
{
NSLog(@"test : send message = %@", word);
}
@end
如果我在viewDidLoad方法中创建Message对象并调用sendMessage方法:
- (void)viewDidLoad {
[super viewDidLoad];
Message *message = [Message new];
[message sendMessage:@"hi,dashen"];
}
控制台会打印以下信息:test : send message = hi,dashen
但现在我将原来sendMessage方法实现给注释掉,覆盖resolveInstanceMethod方法:
#pragma mark - Method Resolution
//- (void)sendMessage:(NSString *)word
//{
// NSLog(@"test : send message = %@", word);
//}
/// override resolveInstanceMethod or resolveClassMethod for changing sendMessage method implementation
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(sendMessage:)) {
class_addMethod([self class], sel, imp_implementationWithBlock(^(id self, NSString *word) {
NSLog(@"resolveInstanceMethod : send message = %@", word);
}), "v@*");
}
return YES;
}
控制台就会打印以下信息:resolveInstanceMethod : send message = hi,dashen
如果目标对象实现- forwardingTargetForSelector:方法,系统就会在运行时调用这个方法,只要这个方法返回的不是nil或self,也会重启消息发送的过程,把这消息转发给其他对象来处理。否则,就会继续Normal Fowarding。
继续上面Message类的例子,将sendMessage和resolveInstanceMethod方法注释掉,然后添加forwardingTargetForSelector方法的实现:
#pragma mark - Fast Forwarding
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if (aSelector == @selector(sendMessage:)) {
return [ClassTestB new];
}
return nil;
}
ClassTestB 是一个实现了sendMessage:的类。
最后只有使用Normal Forwarding来进行消息转发。它首先调用methodSignatureForSelector:方法来获取函数的参数和返回值,如果返回为nil,程序会Crash掉,并抛出unrecognized selector sent to instance异常信息。如果返回一个函数签名,系统就会创建一个NSInvocation对象并调用-forwardInvocation:方法。
继续前面的例子,将forwardingTargetForSelector方法注释掉,添加methodSignatureForSelector和forwardInvocation方法的实现:
#pragma mark - Normal Forwarding
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSMethodSignature *methodSignature = [super methodSignatureForSelector:aSelector];
if (!methodSignature) {
methodSignature = [NSMethodSignature signatureWithObjCTypes:"v@:*"];
}
return methodSignature;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
MessageForwarding *messageForwarding = [MessageForwarding new];
if ([messageForwarding respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:messageForwarding];
}
}
三种方法理论上都可以,但是实际做的时候有一定困难,我们为了防止工程因找不到方法崩溃,会在给nsobject类添加上述三种保护,前提是这三种方法 都需要明确知道方法名称,我们只能在最后一种方法里,debug下加个nsasset报个断言,意义不大。上述三种方法对于理解运行时还是有点好处的。不喜勿喷,因为三种方法 对于捕获崩溃是有用的,修复使不上力。