unrecognized selector sent to instance 闪退问题避免

调用方法时,如果在message方法在receiver对象的类继承体系中没有找到方法,那怎么办?一般情况下,程序在运行时就会Crash掉,抛出unrecognized selector sent to…类似这样的异常信息。但在抛出异常之前,还有三次机会按以下顺序让你拯救程序。如下图所示:

unrecognized selector sent to instance 闪退问题避免_第1张图片
selector.png

1、在发送消息的对象或其父类中动态增加没有实现的方法(假如没有实现的方法是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防护

你可能感兴趣的:(unrecognized selector sent to instance 闪退问题避免)