美文网首页
部分小数类型NSNumber转NSString失精问题的思考和解

部分小数类型NSNumber转NSString失精问题的思考和解

作者: ask_ME | 来源:发表于2019-08-19 16:34 被阅读0次

    由于公司后台数据部分json数据格式为float,导致json转为字典中float对应类型NSNumber极有可能会失精.主要原因在于iOSjson解析库将数值类型解析成为long或者double类型,然后再格式转换为number.
    根本原因在于 numberdescription 方法转成字符串会失精,目前尝试过重写 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
    
    

    相关文章

      网友评论

          本文标题:部分小数类型NSNumber转NSString失精问题的思考和解

          本文链接:https://www.haomeiwen.com/subject/svxesctx.html