调用方法时,如果在message方法在receiver对象的类继承体系中没有找到方法,那怎么办?一般情况下,程序在运行时就会Crash掉,抛出unrecognized selector sent to…类似这样的异常信息。但在抛出异常之前,还有三次机会按以下顺序让你拯救程序。如下图所示:
selector.png1、在发送消息的对象或其父类中动态增加没有实现的方法(假如没有实现的方法是sendMessage:)。需要在发送消息的对象中重写或其父类(NSObject)中重写+ (BOOL)resolveInstanceMethod:(SEL)sel; / + (BOOL)resolveClassMethod:(SEL)sel;,如果是实例方法会调用resolveInstanceMethod,如果这个方法是一个类方法,就会调用resolveClassMethod。
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(sendMessage:)) {
class_addMethod([self class], sel, imp_implementationWithBlock(^(id self, NSString *word) {
NSLog(@"method resolution way : send message = %@", word);
}), "v@*");
}
return YES;
}
2、如果如上方法返回NO, 则调用如下方法,并委托其他类进行处理该方法。假如MessageForwarding已经实现了sendMessage:方法。
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if (aSelector == @selector(sendMessage:)) {
return [MessageForwarding new];
}
return nil;
}
3、如果没有使用Fast Forwarding来消息转发,最后只有使用Normal Forwarding来进行消息转发。它首先调用methodSignatureForSelector:方法来获取函数的参数和返回值,并构造一个NSMethodSignature对象返回,并以此作为forwardInvocation的参数传递给forwardInvocation。如果methodSignatureForSelector返回为nil,程序会Crash掉,并抛出unrecognized selector sent to instance异常信息。如果返回一个函数签名,系统就会创建一个NSInvocation对象并调用-forwardInvocation:方法。
- (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];
}
}
明白了处理逻辑后,可以在如上三个步骤中的任何一个进行处理,这里选择的是在步骤2总进行处理,因为3里面用到了 NSInvocation 对象,此对象性能开销较大,而且这种异常如果出现必然频次较高。最适合将消息转发到一个备用者对象上。
-(id)forwardingTargetForSelector:(SEL)aSelector{
HSProtectHandler *protectHandler = [HSProtectHandler new];
class_addMethod([HSProtectHandler class], aSelector, [protectHandler methodForSelector:@selector(handleSelector:)], "v@:");
return protectHandler;
}
参考:
iOS中的unrecognized selector sent to instance
轻松学习之 Objective-C消息转发
iOS中的crash防护
网友评论