背景
線上崩潰排查中,發(fā)現(xiàn)一批model轉(zhuǎn)JSON時發(fā)生崩潰,描述如下:

崩潰信息
Invalid number value (infinite) in JSON write
XXXX -[NSObject(YYModel) modelToJSONData] (NSObject+YYModel.m:)
崩潰原因
INFINITY:無窮
NAN:不是數(shù)字
使用系統(tǒng)解析方法NSJSONSerialization解析JSON時,會發(fā)生崩潰(如上崩潰日志)。
為什么會產(chǎn)生NAN/INFINITY?
- App作圖記錄,是全平臺的互通的,JS或其它語言支持NAN數(shù)字
- 未知原因混進來或錯誤生成一些NAN,INFINITY數(shù)據(jù)
注:
浮點數(shù)類型除以0 產(chǎn)生 +inf
錯誤開方產(chǎn)生NaN

image.png
如何防護
ios提供了檢測無效數(shù)字的函數(shù)
- isnan(number)
- isinf(number)
在YYModel中,已經(jīng)添加了對無效數(shù)字的檢測(第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解析同樣
// 數(shù)組解析
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)},確實已經(jīng)通過if (isnan(doubleValue) || isinf(doubleValue))返回0避免崩潰。
但當value為數(shù)組,數(shù)組內(nèi)包含無效數(shù)字時,@{@"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解析同樣
// 數(shù)組解析
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;
}
}