由于公司后台数据部分json
数据格式为float
,导致json
转为字典中float
对应类型NSNumber
极有可能会失精.主要原因在于iOS
的json
解析库将数值类型解析成为long
或者double
类型,然后再格式转换为number
.
根本原因在于 number
的 description
方法转成字符串会失精,目前尝试过重写 NSNumber
或作者其元类的description
方法,并没有走这个方法.有哪位盆友知晓请不吝赐教。肯定比下面方法更好啦!
话说Andriod
完全ok
重要的事情说三遍: json
数据请用万能的字符串
补充:iOS13 字面量取值又失精了(需替换objectForKeyedSubscript:
)
测试数据举例: @(90.01)
NSLog(@"%@",@(90.01)); // 90.01000000000001
由于需要修改的地方较多,所以创建NSDictinary
分类,直接覆盖objectForKey
方法
#import "NSDictionary+extend.h"
#import <objc/runtime.h>
@implementation NSDictionary (extend)
+ (void)exchangeInstanceMethod:(Class)anClass method1Sel:(SEL)originalSEL method2Sel:(SEL)SwizzledSEL{
Method originalMethod = class_getInstanceMethod(anClass, originalSEL);
Method SwizzledMethod = class_getInstanceMethod(anClass, SwizzledSEL);
IMP originalIMP = method_getImplementation(originalMethod);
IMP SwizzledIMP = method_getImplementation(SwizzledMethod);
Class class = [self class];
BOOL isSuccess = class_addMethod(class, originalSEL, SwizzledIMP, method_getTypeEncoding(SwizzledMethod));
if (isSuccess) {
class_replaceMethod(class, SwizzledSEL, originalIMP, method_getTypeEncoding(originalMethod));
}else{
method_exchangeImplementations(originalMethod, SwizzledMethod);
}
}
+ (void) load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL originalSEL = @selector(objectForKey:);
SEL SwizzledSEL = @selector(my_objectForKey:);
// __NSDictionaryI 为 NSDictionary 在内存中对应的元类
[self exchangeInstanceMethod:NSClassFromString(@"__NSDictionaryI") method1Sel:originalSEL method2Sel:SwizzledSEL];
// iOS13
SEL originalSELSubscript = @selector(objectForKeyedSubscript:);
SEL SwizzledSELSubscript = @selector(my_objectForKeyedSubscript:);
[self exchangeInstanceMethod:NSClassFromString(@"__NSDictionaryI") method1Sel:originalSELSubscript method2Sel:SwizzledSELSubscript];
});
}
- (id)my_objectForKey:(id)aKey{
id object = [self my_objectForKey:aKey];
if ([object isKindOfClass:[NSNumber class]]) {
if (strcmp( type, @encode(double)) == 0) {
NSString * doubleStr = [NSString stringWithFormat:@"%f", [(NSNumber *)object doubleValue]];
NSDecimalNumber * decNum = [NSDecimalNumber decimalNumberWithString:doubleStr];
object = decNum;
}
}
return object;
}
// 适配 iOS13,需要替换字面量方法
- (id)my_objectForKeyedSubscript:(id)aKey{
id object = [self my_objectForKeyedSubscript:aKey];
if ([object isKindOfClass:[NSNumber class]]) {
const char * type = [object objCType];
if (strcmp( type, @encode(double)) == 0) {
NSString * doubleStr = [NSString stringWithFormat:@"%f", [(NSNumber *)object doubleValue]];
NSDecimalNumber * decNum = [NSDecimalNumber decimalNumberWithString:doubleStr];
object = decNum;
}
}
return object;
}
大功告成啦~~~
说多了都是眼泪,相信大家使用MJExtension
的同学还是很多的,发现此方法在iOS10
是可以的,然而iOS12
就沙雕了,所以我又改了MJExtension
源码(竟然是核心代码).
@implementation NSObject (MJKeyValue)
/**
核心代码:
*/
- (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context
if (propertyClass == [NSString class]) {
// NSNumber -> NSString
if ([value isMemberOfClass:[NSDecimalNumber class]]) {
value = [value description];
}else if ([value isKindOfClass:[NSNumber class]]){
NSString * temp = [NSString stringWithFormat:@"%lf",[(NSNumber *)value doubleValue]];
value = [NSDecimalNumber decimalNumberWithString:temp].stringValue;
} else if ([value isKindOfClass:[NSURL class]]) {
// NSURL -> NSString
value = [value absoluteString];
}
// 以下是修改前的代码
// // NSNumber -> NSString
// if ([value isKindOfClass:[NSNumber class]]){
// value = [value description];
// } else if ([value isKindOfClass:[NSURL class]]) {
// // NSURL -> NSString
// value = [value absoluteString];
// }
}
}
@end