unrecognized selector sent to instance 閃退問題避免

調用方法時,如果在message方法在receiver對象的類繼承體系中沒有找到方法,那怎么辦?一般情況下,程序在運行時就會Crash掉,拋出unrecognized selector sent to…類似這樣的異常信息。但在拋出異常之前,還有三次機會按以下順序讓你拯救程序。如下圖所示:

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防護

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容