在没有热更新的今天,相信大家都有在千辛万苦的把项目上线后,因为后台种种数据类型不符合的问题导致自己的App强行奔溃。看到这里,请对自己说,“别特么的相信后台“。一旦前端反馈出的问题,不懂技术的老板已经把锅盖扣在您亲爱的脑子上了,还怎么愉快的编写代码。不过在开发中,对Crash的情况做友好化确实是必不可少的。但是大部分常见Crash都来至字典、数组这两种数据类型,今天介绍的是几种常见的防崩手段,包括:
1.数组越界导致的崩溃
2.数组、字典有空类型时导致的崩溃
3.向Null类型取值(向一个类中调用该类不存在的方法)
其实能导致崩溃的为题还是有挺多的,一时半会想不起来。对于导致崩溃的bug,个人认为:
1.靠直觉;
2.靠对日志原因打印的分析,然后在靠直觉;
3.特么的还直觉啊?感觉断点调试啊。
-
对于上面的问题,百度一下看起来,真的,一大堆。但是这里总结的,能够一步到位。
数组越界导致的崩溃 和 数组、字典有空类型时导致的崩溃
以下为添加类目的方式防止数组越界导致的崩溃,通过runtime捕获的异常,进入用自己写的方法替代,从而达到防止崩溃的效果。虽然比较长,但是适配了iOS9到iOS11数组越界奔溃时的名称。
+ (void)load{
[super load];
// 替换不可变数组中的方法
Method oldObjectAtIndex = class_getInstanceMethod(objc_getClass("__NSSingleObjectArrayI"), @selector(objectAtIndex:));
Method newObjectAtIndex = class_getInstanceMethod(objc_getClass("__NSSingleObjectArrayI"), @selector(__nickyTsui__objectAtIndex:));
Method oldObjectAtIndex1 = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
Method newObjectAtIndex1 = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(__nickyTsui2__objectAtIndex:));
Method oldObjectAtIndex2 = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndexedSubscript:));
Method newObjectAtIndex2 = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(__nickyTsui3__objectAtIndex:));
Method oldObjectAtIndex4 = class_getInstanceMethod(objc_getClass("__NSArrayM"), @selector(objectAtIndex:));
Method newObjectAtIndex4 = class_getInstanceMethod(objc_getClass("__NSArrayM"), @selector(__nickyTsui5__objectAtIndex:));
method_exchangeImplementations(oldObjectAtIndex, newObjectAtIndex);
method_exchangeImplementations(oldObjectAtIndex1, newObjectAtIndex1);
method_exchangeImplementations(oldObjectAtIndex2, newObjectAtIndex2);
method_exchangeImplementations(oldObjectAtIndex4, newObjectAtIndex4);
if (([UIDevice currentDevice].systemVersion.floatValue < 10.0f)){
Method oldObjectAtIndex3 = class_getInstanceMethod(objc_getClass("__NSCFArray"), @selector(objectAtIndex:));
Method newObjectAtIndex3 = class_getInstanceMethod(objc_getClass("__NSCFArray"), @selector(__nickyTsui4__objectAtIndex:));
method_exchangeImplementations(oldObjectAtIndex3, newObjectAtIndex3);
}
}
- (id)__nickyTsui__objectAtIndex:(NSUInteger)index{
if (index > self.count - 1 || !self.count){
@try {
return [self __nickyTsui__objectAtIndex:index];
} @catch (NSException *exception) {
//__throwOutException 抛出异常
// NSLog(@"数组越界...");
return @"";
} @finally {
}
}
else{
return [self __nickyTsui__objectAtIndex:index];
}
}
- (id)__nickyTsui2__objectAtIndex:(NSUInteger)index{
if (index > self.count - 1 || !self.count){
@try {
return [self __nickyTsui2__objectAtIndex:index];
} @catch (NSException *exception) {
//__throwOutException 抛出异常
// NSLog(@"数组越界...");
return @"";
} @finally {
}
}
else{
return [self __nickyTsui2__objectAtIndex:index];
}
}
- (id)__nickyTsui3__objectAtIndex:(NSUInteger)index{
if (index > self.count - 1 || !self.count){
@try {
return [self __nickyTsui3__objectAtIndex:index];
} @catch (NSException *exception) {
//__throwOutException 抛出异常
// NSLog(@"数组越界...");
return @"";
} @finally {
}
}
else{
return [self __nickyTsui3__objectAtIndex:index];
}
- (id)__nickyTsui4__objectAtIndex:(NSUInteger)index{
if (index > self.count - 1 || !self.count){
@try {
return [self __nickyTsui4__objectAtIndex:index];
} @catch (NSException *exception) {
//__throwOutException 抛出异常
// NSLog(@"数组越界...");
return @"";
} @finally {
}
}
else{
return [self __nickyTsui4__objectAtIndex:index];
}
}
- (id)__nickyTsui5__objectAtIndex:(NSUInteger)index{
if (index > self.count - 1 || !self.count){
@try {
return [self __nickyTsui5__objectAtIndex:index];
} @catch (NSException *exception) {
//__throwOutException 抛出异常
// NSLog(@"数组越界...");
return @"";
} @finally {
}
}
else{
return [self __nickyTsui5__objectAtIndex:index];
}
}
如果觉得这种方式太长还有一个是大神编写的第三方:AvoidCrash
据说有了这个工具,妈妈再也不当心App会崩溃了。简单易用,一句话开启防崩模式。
具体如下:
-
1.最好直接pod安装或者直接拖进项目
①.pod 'AvoidCrash'
②.附上大神github
https://github.com/chenfanfang/AvoidCrash
-
2.在AppDelegate.h 导入头文件#import "AvoidCrash.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// 1 开始监听
[AvoidCrash becomeEffective];
// 2 监听通知:AvoidCrashNotification, 获取AvoidCrash捕获的崩溃日志的详细信息
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dealwithCrashMessage:) name:AvoidCrashNotification object:nil]; return YES;
}
- (void)dealwithCrashMessage:(NSNotification *)note {
//注意:所有的信息都在userInfo中
//你可以在这里收集相应的崩溃信息进行相应的处理(比如传到自己服务器)
NSLog(@"%@",note.userInfo);
}
-
3.在代码里埋bug
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *testArr = @[@"1",@"2",@"3"];
NSLog(@"_______%@_______",testArr[8]);
}
-
4.command + R 运行,发现再也不闪退了。
不过AvoidCrash也不是万能的,当后台返回了Null类型,我们在不知该字段对应着Null的值,然后向Null类型取值或调用方法时,还是会崩溃的。
-
这时,又一神器就要出来了。某国外大神针对Null类型的崩溃情况,当像Null调用方法时,将Null转换成了nil,nil对方法的各种调用是不会崩溃的。
附上github地址:
https://github.com/nicklockwood/NullSafe
-
只需要把.m文件拖进项目,其他任何操作都不需要,就能生效了,喜欢吗!
-
然后在代码里埋bug
- (void)viewDidLoad {
[super viewDidLoad];
NSDictionary *dic = @{@"123":[NSNull new]};
NSLog(@"______%@______",dic[@"123"][0]);
}
-
然后发现,从一个NSNull对象中objectAtIndex:去取值会发现不会崩溃的。
网友评论