一、NSException簡(jiǎn)介
1.什么是NSException?
說到NSException你可能不太了解,但是下面的這張圖你肯定見過不止一次

這些就是NSException產(chǎn)生的,一旦程序拋出異常,程序就會(huì)崩潰,控制臺(tái)就會(huì)有這些崩潰日志。
2、定義一個(gè)NSException對(duì)象并拋出
NSString*name =@"exception name";
NSString*reason =@"exception reason";
NSException*exception = [NSException exceptionWithName:name reason:reason userInfo:nil];
@throw exception;
運(yùn)行程序會(huì)發(fā)現(xiàn)輸出和上圖類似的日志:

二、異常的簡(jiǎn)單處理
如果你在開發(fā)過程中可能對(duì)某段代碼不信任,也就是有崩潰的可能,而在線上直接報(bào)錯(cuò)又會(huì)影響又會(huì)體驗(yàn),那你就可以通過如下的方法處理:
NSMutableArray*array = [NSMutableArray array];
NSString*nilStr =nil;
@try{
//有可能會(huì)出現(xiàn)異常的代碼,這里寫的代碼一定會(huì)出現(xiàn)問題
[array insertObject:nilStr atIndex:0];
}@catch(NSException *exception) {
//如果@try中的代碼出現(xiàn)異常,就會(huì)執(zhí)行這里的代碼,也就可以在這里進(jìn)行相應(yīng)操作
NSLog(@"exception.name=%@,exception.reason=%@",exception.name,exception.reason);
//如果想要拋出異常就執(zhí)行如下代碼,程序就會(huì)崩潰,便于調(diào)試
// @throw exception;
}@finally{
//這里的代碼一定會(huì)執(zhí)行
}
三、防止?jié)撛诘谋罎L(fēng)險(xiǎn)
如果你并不知道程序運(yùn)行到哪里會(huì)出現(xiàn)異常,或者說對(duì)于Foundation框架里有非常多常用的方法有導(dǎo)致崩潰的潛在危險(xiǎn),那么該如何攔截潛在的異常風(fēng)險(xiǎn),并進(jìn)行相應(yīng)的處理,防止崩潰的出現(xiàn)呢?
解決辦法是:
1.利用iOS的runtime特性和catalog添加新方法,替換掉系統(tǒng)的存在異常風(fēng)險(xiǎn)的方法。
2.利用異常捕獲防止程序崩潰,并進(jìn)行相應(yīng)處理。
具體的步驟:
1.創(chuàng)建一個(gè)HandleCrash類:
TXHandleCrash.h
#import
#define HandleCrashLogBegin @"==========================handleCrashLogBegin==========================="
#define HandleCrashLogEnd @"=============================handleCrash==============================="
#define HandleCrashLogNotification @"HandleCrashLogNotification"
@interfaceTXHandleCrash :NSObject
/**
start handle crash
*/
+(void)startHandle;
/**
exchange class method
*/
+(void)handleClass:(Class)anClass exchangeClassMethod:(SEL)method1 Method:(SEL)method2;
/**
exchange instance method
*/
+(void)handleClass:(Class)anClass exchangeCInstanceMethod:(SEL)method1 Method:(SEL)method2;
/**
handle exception
@param remark remark
*/
+(void)handleException:(NSException*)exception remark:(NSString*)remark;
@end
TXHandleCrash.m
#import"TXHandleCrash.h"
#import
#import"NSDictionary+TXHandleCrash.h"
@implementationTXHandleCrash
+(void)startHandle
{
dispatch_once_ttoken;
dispatch_once(&token , ^{
[NSDictionaryhandleCrash];
});
}
+(void)handleClass:(Class)anClass exchangeClassMethod:(SEL)method1 Method:(SEL)method2
{
Methodmtd1 =class_getClassMethod(anClass, method1);
Methodmtd2 =class_getClassMethod(anClass, method2);
method_exchangeImplementations(mtd1, mtd2);
}
+(void)handleClass:(Class)anClass exchangeCInstanceMethod:(SEL)method1 Method:(SEL)method2
{
Methodmtd1 =class_getInstanceMethod(anClass, method1);
Methodmtd2 =class_getInstanceMethod(anClass, method2);
method_exchangeImplementations(mtd1, mtd2);
}
+(void)handleException:(NSException*)exception remark:(NSString*)remark
{
//堆棧數(shù)據(jù)
NSArray*callStackSymbols = [NSThreadcallStackSymbols];
//獲取在哪個(gè)類的哪個(gè)方法中實(shí)例化的數(shù)組,并格式化:-[類名方法名]、+[類名方法名]
NSString*locationMsg = [selflocationExcptionThroughCallStackSymbols:callStackSymbols];
if(!locationMsg) {
locationMsg =@"崩潰位置定位失敗,請(qǐng)查看函數(shù)調(diào)用棧排查錯(cuò)誤";
}
NSString*exceptionName = exception.name;
NSString*exceptionReason = exception.reason;
NSString*exceptionLocation = [NSStringstringWithFormat:@"exception location:%@",locationMsg];
NSString*exceptionMsg = [NSStringstringWithFormat:@"\n\n%@\n\n%@\n%@\n%@\n%@\n\n%@\n\n",HandleCrashLogBegin, exceptionName, exceptionReason, exceptionLocation, remark,HandleCrashLogEnd];
NSLog(@"%@", exceptionMsg);
NSDictionary*exceptionInfoDic =@{
@"exceptionName": exceptionName,
@"exceptionReason": exceptionReason,
@"exceptionLocation": exceptionLocation,
@"remark": remark,
@"exception": exception,
@"callStackSymbols": callStackSymbols
};
//將錯(cuò)誤信息放在字典里,用通知的形式發(fā)送出去
[[NSNotificationCenterdefaultCenter]postNotificationName:HandleCrashLogNotificationobject:niluserInfo:exceptionInfoDic];
}
+(NSString*)locationExcptionThroughCallStackSymbols:(NSArray*)callStackSymbols
{
__blockNSString*locationMsg =nil;
NSLog(@"callStackSymbols=%@",callStackSymbols);
//通過正則匹配出的格式為,-[類名方法名]、+[類名方法名]
NSString*regularExpStr =@"[-\\+]\\[.+\\]";
NSRegularExpression*regularExp = [[NSRegularExpressionalloc]initWithPattern:regularExpStroptions:NSRegularExpressionCaseInsensitiveerror:nil];
for(intindex =2; index
NSString*callStackSymbol = callStackSymbols[index];
[regularExpenumerateMatchesInString:callStackSymboloptions:NSMatchingReportProgressrange:NSMakeRange(0, callStackSymbol.length)usingBlock:^(NSTextCheckingResult*_Nullableresult,NSMatchingFlagsflags,BOOL*_Nonnullstop) {
if(result) {
NSString*tmpLocationMsg = [callStackSymbolsubstringWithRange:result.range];
//get class name
NSString*className = [tmpLocationMsgcomponentsSeparatedByString:@" "].firstObject;
className = [classNamecomponentsSeparatedByString:@"["].lastObject;
NSBundle*bundle = [NSBundlebundleForClass:NSClassFromString(className)];
//filter catalog and system Class
if(![classNamehasPrefix:@")"] && bundle == [NSBundlemainBundle]) {
locationMsg = tmpLocationMsg ;
}
*stop =YES;
}
}];
if(locationMsg.length) {
break;
}
}
returnlocationMsg ;
}
@end
2.創(chuàng)建一個(gè)NSDictionary的catalog
NSDictionary+TXHandleCrash.h
#import
@interfaceNSDictionary (TXHandleCrash)
+(void)handleCrash;
@end
NSDictionary+TXHandleCrash.m
#import"NSDictionary+TXHandleCrash.h"
#import"TXHandleCrash.h"
@implementationNSDictionary (TXHandleCrash)
+(void)handleCrash
{
[TXHandleCrashhandleClass:[selfclass]exchangeClassMethod:@selector(dictionaryWithObjects:forKeys:count:)Method:@selector(handleCrashDictionaryWithObjects:forKeys:count:)];
}
+(instancetype)handleCrashDictionaryWithObjects:(constid_Nonnull__unsafe_unretained*)objects forKeys:(constid_Nonnull__unsafe_unretained*)keys count:(NSUInteger)cnt
{
idinstance =nil;
@try{
instance = [selfhandleCrashDictionaryWithObjects:objectsforKeys:keyscount:cnt];
}@catch(NSException *exception) {
[TXHandleCrashhandleException:exceptionremark:@""];
NSUIntegerindex =0;
id_Nonnull__unsafe_unretainednewObjects[cnt];
id_Nonnull__unsafe_unretainednewKeys[cnt];
for(inti =0; i
if(keys[i] && objects[i]) {
newObjects[index] = objects[i];
newKeys[index] = keys[i];
index ++ ;
}
}
instance = [selfhandleCrashDictionaryWithObjects:newObjectsforKeys:newKeyscount:index];
}@finally{
returninstance ;
}
}
@end
3.在Appdelegate中
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
[TXHandleCrashstartHandle];
[[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(handleException:)name:HandleCrashLogNotificationobject:nil];
returnYES;
}
-(void)handleException:(NSNotification*)notif
{
NSDictionary*exceptionInfo = notif.userInfo;
NSLog(@"----------%@",exceptionInfo);
}
4.在viewcontroller中
- (void)viewDidLoad {
[superviewDidLoad];
NSString*nilStr =nil;
//通過這種方法創(chuàng)建字典其實(shí)是調(diào)用dictionaryWithObjects:forKeys:count:方法,如果不做任何處理下面的代碼就會(huì)直接崩潰,現(xiàn)在經(jīng)常上面那么多的處理就不會(huì)崩潰了,并且可以通過AppDelegate中的通知做到收集崩潰日志的目的
NSDictionary*dictionary =@{@"key1":nilStr,@"key2":@"values"};
NSLog(@"dic=%@",dictionary);
}