NO.1:NSInvalidArgumentException 异常
出现这个crash的原因较多,选取了几个崩溃次数较多的crash.
crash日志1:
-[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[1]'
crash日志拿到了,怎么复现该现象呢?我们看到initWithObjects:forKeys:count:,猜测一下应该是NSDictionary初始化时的问题,在看后面的提示attempt to insert nil object,此时就可以做一个猜测,应该是NSDictionary初始化时插入nil对象造成的异常。下面我们写一段代码来验证一下:
运行后崩溃的信息如下:
上面的崩溃信息验证我们的想法,为什么会出现这种现象呢?如何解决这样的crash呢?
在转换成NSDictionary的过程中,后台返回的数据有时可能为空,就会造成插入nil对象,从而导致crash。
目前个人知道的解决办法:
1.后台在返回数据的时候进行校验,对空值进行处理。但是在项目中有些空值是有特殊的用途,此种方案不可行。
2.在转换成NSDictionary的时候,对后台返回的数据进行校验,把空值转换成NSNull对象。方案可行,但是需要对现有代码做大的改动,每次转换的时候都需要进行校验,太麻烦。业务高速发展时期,这样做成本太高。
3.有没有一种无须改动现有代码又能解决该问题呢?答案是有的,可以利用Objective-C的runtime来解决该问题。
NSDictionary插入nil对象会造成崩溃,但是插入NSNull对象是不会造成崩溃的,只要利用runtime的Swizzle Method把nil对象给转换成NSNull对象就可以把该问题给解决了。创建一个NSDictionary的类别,利用runtime的Swizzle Method来替换系统的方法。源码实现可以参考Glow团队封装的NSDictionary+NilSafe(Github上可下载到), 全部源码会在文章末尾提供
crash 日志2
data parameter is nil
NSData *data = nil;
NSError *error;
NSDictionary *orginDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
NSLog(@
"originDict is : %@"
, orginDict);
这个问题比较好解决,在序列化的时候,统一加入判断,判断data是不是nil即可。
crash 日志3
1
|
unrecognized selector sent to instance 0x15d23910
|
造成这条崩溃的原因,想必大家都比较熟悉了,就是一个类调用了一个不存在的方法,造成的崩溃。解决这样的问题,可以在写一个方法的时候,判断一下其类的类型,不符合类型的不让其调用,也可以使用runtime对常见的方法调用做一下错误兼容。比如我这边经常会出现这样的崩溃:
1
2
3
4
|
-[__NSCFConstantString objectForKeyedSubscript:]: unrecognized selector sent to instance 0x1741af420
-[NSNull length]: unrecognized selector sent to instance 0x1b21e6ef8
-[__NSCFConstantString objectForKeyedSubscript:]: unrecognized selector sent to instance
-[__NSDictionaryI length]: unrecognized selector sent to instance 0x174264500
|
当这些对象调用这几个不存在的方法的时候,替换成自己定义的一个方法,对它们做一下错误兼容,使应用不会崩溃。现截取部分代码实现,全部源码会在文章末尾提供。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@implementation NSString (NSRangeException)
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
@autoreleasepool {
[objc_getClass(
"__NSCFConstantString"
) swizzleMethod:@selector(objectForKeyedSubscript:) swizzledSelector:@selector(replace_objectForKeyedSubscript:)];
}
});
}
- (id)replace_objectForKeyedSubscript:(NSString *)key {
return
nil;
}
@end
|
小结一下,造成NSInvalidArgumentException异常大概有以下原因:
-
NSDictionary插入nil的对象。NSMutableDictionary也是同样的道理。
-
NSJSONSerialization序列化的时候,传入data为nil。
-
an unrecognized selector 无法识别的方法
-