背景
线上崩溃排查中,发现一批model转JSON时发生崩溃,描述如下:
崩溃信息Invalid number value (infinite) in JSON write
XXXX -[NSObject(YYModel) modelToJSONData] (NSObject+YYModel.m:)
崩溃原因
INFINITY:无穷
NAN:不是数字
使用系统解析方法NSJSONSerialization解析JSON时,会发生崩溃(如上崩溃日志)。
为什么会产生NAN/INFINITY?
- App作图记录,是全平台的互通的,JS或其它语言支持NAN数字
- 未知原因混进来或错误生成一些NAN,INFINITY数据
注:
浮点数类型除以0 产生 +inf
错误开方产生NaN
如何防护
ios提供了检测无效数字的函数
- isnan(number)
- isinf(number)
在YYModel中,已经添加了对无效数字的检测(第6行),为什么还是崩了呢?
static id ModelToJSONObjectRecursive(NSObject *model) {
if (!model || model == (id)kCFNull) return model;
if ([model isKindOfClass:[NSString class]]) return model;
if ([model isKindOfClass:[NSNumber class]]) {
double doubleValue = ((NSNumber *)model).doubleValue;
if (isnan(doubleValue) || isinf(doubleValue)) {
return @(0);
}
return model;
}
...
...
// Set解析同样
// 数组解析
if ([model isKindOfClass:[NSArray class]]) {
if ([NSJSONSerialization isValidJSONObject:model]) return model;
NSMutableArray *newArray = [NSMutableArray new];
for (id obj in (NSArray *)model) {
if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {
[newArray addObject:obj];
} else {
id jsonObj = ModelToJSONObjectRecursive(obj);
if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];
}
}
return newArray;
}
}
上面的解析,对于dict中value为Number类型,@{key: @(NAN)},确实已经通过if (isnan(doubleValue) || isinf(doubleValue))返回0避免崩溃。
但当value为数组,数组内包含无效数字时,@{@"v1": @[@(NAN),@(INFINITY)]},就直接绕过了isnan/isinf的检测(走第19行),导致解析崩溃。
修复
去除NSSet/NSArray中的NSNumber判定(19行),让其走isnan/isinf检测。
static id ModelToJSONObjectRecursive(NSObject *model) {
if (!model || model == (id)kCFNull) return model;
if ([model isKindOfClass:[NSString class]]) return model;
if ([model isKindOfClass:[NSNumber class]]) {
double doubleValue = ((NSNumber *)model).doubleValue;
if (isnan(doubleValue) || isinf(doubleValue)) {
return @(0);
}
return model;
}
...
...
// Set解析同样
// 数组解析
if ([model isKindOfClass:[NSArray class]]) {
if ([NSJSONSerialization isValidJSONObject:model]) return model;
NSMutableArray *newArray = [NSMutableArray new];
for (id obj in (NSArray *)model) {
if ([obj isKindOfClass:[NSString class]]) { // 去除NSNumber判定,让其走isnan/isinf检测
[newArray addObject:obj];
} else {
id jsonObj = ModelToJSONObjectRecursive(obj);
if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];
}
}
return newArray;
}
}
网友评论