一、NSException简介
1.什么是NSException?
说到NSException你可能不太了解,但是下面的这张图你肯定见过不止一次
这些就是NSException产生的,一旦程序抛出异常,程序就会崩溃,控制台就会有这些崩溃日志。
2、定义一个NSException对象并抛出
NSString*name =@"exception name";
NSString*reason =@"exception reason";
NSException*exception = [NSException exceptionWithName:name reason:reason userInfo:nil];
@throw exception;
运行程序会发现输出和上图类似的日志:
二、异常的简单处理
如果你在开发过程中可能对某段代码不信任,也就是有崩溃的可能,而在线上直接报错又会影响又会体验,那你就可以通过如下的方法处理:
NSMutableArray*array = [NSMutableArray array];
NSString*nilStr =nil;
@try{
//有可能会出现异常的代码,这里写的代码一定会出现问题
[array insertObject:nilStr atIndex:0];
}@catch(NSException *exception) {
//如果@try中的代码出现异常,就会执行这里的代码,也就可以在这里进行相应操作
NSLog(@"exception.name=%@,exception.reason=%@",exception.name,exception.reason);
//如果想要抛出异常就执行如下代码,程序就会崩溃,便于调试
// @throw exception;
}@finally{
//这里的代码一定会执行
}
三、防止潜在的崩溃风险
如果你并不知道程序运行到哪里会出现异常,或者说对于Foundation框架里有非常多常用的方法有导致崩溃的潜在危险,那么该如何拦截潜在的异常风险,并进行相应的处理,防止崩溃的出现呢?
解决办法是:
1.利用iOS的runtime特性和catalog添加新方法,替换掉系统的存在异常风险的方法。
2.利用异常捕获防止程序崩溃,并进行相应处理。
具体的步骤:
1.创建一个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
{
//堆栈数据
NSArray*callStackSymbols = [NSThreadcallStackSymbols];
//获取在哪个类的哪个方法中实例化的数组,并格式化:-[类名方法名]、+[类名方法名]
NSString*locationMsg = [selflocationExcptionThroughCallStackSymbols:callStackSymbols];
if(!locationMsg) {
locationMsg =@"崩溃位置定位失败,请查看函数调用栈排查错误";
}
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
};
//将错误信息放在字典里,用通知的形式发送出去
[[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.创建一个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;
//通过这种方法创建字典其实是调用dictionaryWithObjects:forKeys:count:方法,如果不做任何处理下面的代码就会直接崩溃,现在经常上面那么多的处理就不会崩溃了,并且可以通过AppDelegate中的通知做到收集崩溃日志的目的
NSDictionary*dictionary =@{@"key1":nilStr,@"key2":@"values"};
NSLog(@"dic=%@",dictionary);
}