*問題:調(diào)用的方法找不到會怎么辦?如
People *people = [[Book alloc]init];
[people eat];
上面兩句代碼中,People類有一個eat的方法傲茄,但People *people = [[Book alloc]init];返回的people是一個people,這時候people再去執(zhí)行eat方法赂毯,程序就會Carsh怀跛,并會拋出unrecognized selector sent to 的錯誤,空IMP(指針錯誤)相叁,因為Book類里面沒有eat的方法遵绰。
對于這種錯誤,一般的處理處理方式是改變people實現(xiàn)的代碼增淹,讓他生成正確的類型椿访,今天我們來講講另外的一種預(yù)防處理機制,我們暫把它叫做空IMP的runtime處理法
在runtime的機制中虑润,程序在運行時成玫,如果執(zhí)行到IMP的對象時,在拋出unrecognized selector sent to錯誤之前拳喻,程序還會執(zhí)行三個方法哭当,我們暫且稱為空指針三步走。
主要用到objc.h的四個方法:
+ (BOOL)resolveInstanceMethod:(SEL)selOBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
- (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");
這四個方法為我們提供了三種補救方案冗澈,
第一種方案:Resolution嘗試新的解決方案钦勘,若有方案,則重啟消息發(fā)送流程亚亲,代碼如下
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if([NSStringFromSelector(sel) isEqualToString:eat]){
class_addMethod(self.class,NSSelectorFromString(eat), (IMP)eat,"@:");//給對象添加一個方法
return [super resolveInstanceMethod:sel];//重新發(fā)送
}
return NO;
}
上面代碼中彻采,我們在條件檢測中去辨別是不是我們要處理的方法,如果是捌归,我們就用runtime動態(tài)增加一個新方法肛响,并重啟消息發(fā)送機制[super resolveInstanceMethod:sel],如不是我們要處理的方法惜索,則返回NO特笋。
如果在這一步中返回值不為NO,則系統(tǒng)就根據(jù)根據(jù)返回的方法去重新處理门扇,如返回NO雹有,則進去第二部偿渡,實行第二種解決方案
第二種解決方案
//Fast Forwarding向前尋找處理對象
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if([NSStringFromSelector(aSelector) isEqualToString:@"eat"]){
People *people = [[People alloc]init];
NSLog(@"新生成一個People對象%@,讓他來幫我們完成eat的動作",people);
return people;//返回新的對象霸奕,讓心的對象去執(zhí)行新對象中的eat方法
}
return nil;
}
上面代碼中溜宽,我們在條件檢測中去辨別是不是我們要處理的方法,如果是质帅,我們就新生成一個正確的對象peopple适揉,返回新對象peopple,讓新對象peopple去執(zhí)行eat煤惩,如不是我們要處理的方法嫉嘀,則返回nil。
如果這一步?jīng)]有返回對象魄揉,則進去第三種解決方案
第三種解決方案
//Normal Forwarding 這個解決方案又分為兩步走
//第一步
-(NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector
{
if([NSStringFromSelector(aSelector) isEqualToString:@"eat"]){
//是我們要處理的方法
//第一步生成方法信號剪侮,告訴系統(tǒng)我們找到了這個方法的處理方式了
NSMethodSignature *methodSignature = [super methodSignatureForSelector:aSelector];
if(!methodSignature) {
methodSignature = [NSMethod SignaturesignatureWithObjCTypes:"v@:*"];
}
returnmethodSignature;
}
NSLog(@"沒有這個方法的處理方式");
return nil;//返回nil的時候,系統(tǒng)就會拋出unrecognized selector sent to的錯誤洛退。
}
//第二步
- (void)forwardInvocation:(NSInvocation*)anInvocation
{
if([NSStringFromSelector(anInvocation.selector) isEqualToString:@"eat"]){
People *people = [[People alloc]init];//新生成對象
if([people respondsToSelector:anInvocation.selector]) {//判斷對象是否能處理該方法
[anInvocation invokeWithTarget:people];//把動作交給新的對象people去完成
}
}
}
上面代碼瓣俯,第一步中,我們在條件檢測中去辨別是不是我們要處理的方法兵怯,如果是彩匕,則需返回一個信號告訴系統(tǒng),找到處理方式了媒区,如不是我們要處理的方法驼仪,則返回nil,讓系統(tǒng)拋出unrecognized selector sent to的錯誤袜漩。
如果在第一步中有返回信號绪爸,則系統(tǒng)就會執(zhí)行第二步的方法,系統(tǒng)跑到forwardInvocation:
后宙攻,我們調(diào)用[anInvocation invokeWithTarget:people];幫方法的處理者替換掉
*注意毡泻,如果在第一步中有返回信號,系統(tǒng)就不再會拋出unrecognized selector sent to的錯誤粘优,即不管是否實現(xiàn)了第二步,程序都不會奔潰了呻顽。
說了那么多雹顺,這究竟有什么用?
1.退一步處理原則:奔潰是最不好的一種體驗廊遍,就算我們沒有完成用戶的動作嬉愧,最好也不要讓程序奔潰,這里你可以報提示或?qū)㈠e誤信息提交至服務(wù)器喉前。
2.動態(tài)修復(fù)没酣,程序發(fā)布時預(yù)留runtiem處理接口王财,分析服務(wù)器接收到的錯誤信息,利用runtiem進行修復(fù)