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