昨天看到一个群里的朋友的问题,接手一个已有项目的历史遗留bug:项目已经完成,代码量很大,有很多自定义模型类,并且模型类直接存在各种嵌套,之前的模型类所有关于服务器返回的数字都是统一用NSString存储的,但是后台返回的并不是字符串,导致了YYModel字典转模型的时候,所有模型的字符串都是这样:
NSDictionary *dict = @{
@"fee": [NSNumber numberWithDouble:807.69],
@"firend":@{
@"fee": [NSNumber numberWithDouble:807.69]
}
};
后台返回的数字:807.69
// 类似的模型类
@interface Person : NSObject
@property(nonatomic, strong) NSString *fee;
@property(nonatomic, strong) Person *friend;
@end
YYModel字典转模型后的字符串:p.friend.fee = @"807.6900000000001"
修改起来麻烦的情况在于:后台有大量的不同字段名的数据都是这样返回的,而且存在模型套模型、模型套模型数组这些情况,无论是客户端改模型类的类型,还是后台改,都是一个很大的工作量,需要改项目中特别多的地方,改起来又需要重新依次测试,非常的耗时间。
最后我还是想到了一个最取巧的办法,不需要去改动项目的任何代码,只需要创建一个分类文件,用runtime方法交换,在YYModel通过字典给模型赋值数据的方法之前,先将字典的NSNumber类型转成不损失精度的NSString,在尾部去0,重新传给YYModel原来的方法就行了。
只需要将下面这个分类文件添加到项目中,就可以解决这个问题。这样后台和客户端都不需要改任何项目源代码,实现了对项目源代码的0入侵。
源码:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSObject (ModelExchange)
@end
NS_ASSUME_NONNULL_END
#import "NSObject+ModelExchange.h"
#import "NSObject+YYModel.h"
#import <objc/runtime.h>
@implementation NSObject (ModelExchange)
+ (void)load {
Method method1 = class_getInstanceMethod([self class], @selector(yy_modelSetWithDictionary:));
Method method2 = class_getInstanceMethod([self class], @selector(my_modelSetWithDictionary:));
method_exchangeImplementations(method1, method2);
}
- (BOOL)my_modelSetWithDictionary:(NSDictionary *)dic {
NSMutableDictionary *mDictionary = [NSMutableDictionary dictionary];
[dic enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:[NSNumber class]]) {
NSNumber *num = (NSNumber *)obj;
NSNumberFormatter *formatter = [NSNumberFormatter new];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
[formatter setGroupingSeparator:@""];
NSString *str = [formatter stringFromNumber:num];
[mDictionary setValue:str forKey:key];
} else {
[mDictionary setValue:obj forKey:key];
}
}];
return [self my_modelSetWithDictionary:mDictionary];
}
@end