正文
id obj = objects[i];
if (!key) {
continue;
}
if (!obj) {
obj = [NSNull null];
}
safeKeys[j] = key;
safeObjects[j] = obj;
j++;
}
return [self gl_dictionaryWithObjects:safeObjects forKeys:safeKeys count:j];
}
crash 日志1-2
data parameter is nil
通过日志信息,可以把崩溃问题定位到参数为nil的情况,在看了下堆栈的日志信息,把问题定位到了NSJSONSerialization序列化的时候,传入data为nil,造成的崩溃。为了验证是不是该问题,我写了一段代码做了下验证:
NSData *data = nil;
NSError *error;
NSDictionary *orginDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
NSLog(@"originDict is : %@", orginDict);
运行后,崩溃信息如下:
Crash日志 1-2
这个问题比较好解决,在序列化的时候,统一加入判断,判断data是不是nil即可。
crash 日志1-3
unrecognized selector sent to instance 0x15d23910
造成这条崩溃的原因,想必大家都比较熟悉了,就是一个类
调用了一个不存在的方法,造成的崩溃。
解决这样的问题,可以在写一个方法的时候,判断一下其类的类型,不符合类型的不让其调用,也可以使用runtime对常见的方法调用做一下错误兼容。比如我这边经常会出现这样的崩溃:
-[__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
当这些对象调用这几个不存在的方法的时候,替换成自己定义的一个方法,对它们做一下错误兼容,使应用不会崩溃。现截取部分代码实现,全部源码会在文章末尾提供。
@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 无法识别的方法
NSInvalidArgumentException的崩溃记录有149条(总崩溃记录304条),占49.01%,称霸Crash界,杀手排名第一。