处理服务器的json中的数字显示价格

在做商城的时候,经常会涉及到价格,但是服务器一般给过来是数字类型 , json转换之后是NSNumber类型,在把NSNumber转成NSString显示到label.

绝大多数数字都没有问题,但是某些特殊的数字,比如 服务器给过来时 99.99 , 在json解析后也确实是99.99,但是打印log或者显示在label上变成了99.98999999999999 ,  主要是系统是用二进制存贮10进制的数字 , 精度丢失无法避免 . 

现在要想办法让展示出来 , 一开始想法是既然是价格那么肯定只会精确到分 , 所以判断2位小数就可以了 ,  最初的想法 , 给NSNumber写一个category , 在类别中统一处理价格的显示 , 先获取这个数字的小数部分 , 如果小数部分的长度大于4,比如0.989999999这个的数字,那就要格式化字符串输出 %.2lf 即可 , 如果没有小数部分或者小数部分为0 , 输出原值即可.

方法1:

- (NSString *)moneyDescription {
    
    double point = [self doubleValue] - [self integerValue] ;
    NSString * pointStr = [NSString stringWithFormat:@"%lf",point];
    if (pointStr.length > 4 && point>0) {
        return [NSString stringWithFormat:@"%.2lf",self.doubleValue];
    }
    return self.stringValue;
    
 
    
}

本来这个也满足需求了,但是总觉得这样的方法有点傻傻的 , 一顿百度之后,找到了这个NSDecimalNumber, 顾名思义这是一个十进制数字类,继承自NSNumber,苹果针对浮点类型计算精度问题提供出来的计算类,基于十进制的科学计数法来计算,同时可以指定舍入模式,一般用于货币计算。果然还是苹果的开发已经考虑到NSNumber显示的进度丢失. 所以推荐使用方法2.

方法2

- (NSString *)moneyDescription {
        
    double conversionValue = [self doubleValue];
    NSString *doubleString = [NSString stringWithFormat:@"%lf", conversionValue];
    NSDecimalNumber *decNumber = [NSDecimalNumber decimalNumberWithString:doubleString];
    return decNumber.description;
    
    
}

实现之后就在想 , 现在要在每个价格上改一遍用moneyDescription , 为什么不用runtime替换下系统原来的打印方式. 

+ (void)load{
    [self swizzleInstanceMethod:@selector(description) with:@selector(moneyDescription)];
}

+ (BOOL)swizzleInstanceMethod:(SEL)originalSel with:(SEL)newSel {
    Method originalMethod = class_getInstanceMethod(self, originalSel);
    Method newMethod = class_getInstanceMethod(self, newSel);
    if (!originalMethod || !newMethod) return NO;
    
    class_addMethod(self,
                    originalSel,
                    class_getMethodImplementation(self, originalSel),
                    method_getTypeEncoding(originalMethod));
    class_addMethod(self,
                    newSel,
                    class_getMethodImplementation(self, newSel),
                    method_getTypeEncoding(newMethod));
    
    method_exchangeImplementations(class_getInstanceMethod(self, originalSel),
                                   class_getInstanceMethod(self, newSel));
    return YES;
}

替换之后,发现  NSNumber的  description方法 , 在NSLog中没有调用   , 在 [NSString stringWithFormat:@"¥ %@",num]  也是无效的 , 一顿百度+问其他大神 , 发现[NSString stringWithFormat:@"¥ %@",num] 会调用 descriptionWithLocale , 但是NSLog不知道会调到什么方法 , 所以既然写的都是用stringWithFormat , 那就转发到description , (其实local里面什么都没有)


#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
- (NSString *)descriptionWithLocale:(id)locale{
    // local中什么都没有,直接消息转发到description
    //NSLog(@"locale is %@",locale);
    return [self description];
}
#pragma clang diagnostic pop

最后附上完成的类别文件

#import <Foundation/Foundation.h>

@interface NSNumber (EqualString)

@property (nonatomic,readonly,copy) NSString * moneyDescription ;

- (BOOL)isEqualToString:(NSString *)aString;


@end

#import "NSNumber+EqualString.h"
#import <objc/objc.h>
#import <objc/runtime.h>
@implementation NSNumber (EqualString)

- (BOOL)isEqualToString:(NSString *)aString {
    
    return [self.description isEqualToString:aString];
}

- (NSString *)moneyDescription {
    
//    double point = [self doubleValue] - [self integerValue] ;
//    NSString * pointStr = [NSString stringWithFormat:@"%lf",point];
//    if (pointStr.length > 4 && point>0) {
//        return [NSString stringWithFormat:@"%.2lf",self.doubleValue];
//    }
//    return self.stringValue;
    
    NSLog(@"moneyDescription");
    double conversionValue = [self doubleValue];
    NSString *doubleString = [NSString stringWithFormat:@"%lf", conversionValue];
    NSDecimalNumber *decNumber = [NSDecimalNumber decimalNumberWithString:doubleString];
    return decNumber.description;
    
    
}

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
// [NSString stringWithFormat:@"¥ %@",num] , 会调到这个方法
- (NSString *)descriptionWithLocale:(id)locale{
    NSLog(@"locale is %@",locale);
    return [self description];
}
#pragma clang diagnostic pop


// num.description直接调用才管用 ;  [NSString stringWithFormat:@"¥ %@",num] , 不管用
- (NSString *)description {
    NSLog(@"description");
    double point = [self doubleValue] - [self integerValue] ;
    NSString * pointStr = [NSString stringWithFormat:@"%lf",point];
    if (pointStr.length > 4 && point>0) {
        return [NSString stringWithFormat:@"%.2lf",self.doubleValue];
    }
    return self.stringValue;
}
// po 的时候调用
- (NSString *)debugDescription {
    NSLog(@"debugDescription");
    double point = [self doubleValue] - [self integerValue] ;
    NSString * pointStr = [NSString stringWithFormat:@"%lf",point];
    if (pointStr.length > 4 && point>0) {
        return [NSString stringWithFormat:@"%.2lf",self.doubleValue];
    }
    return self.stringValue;
}


+ (void)load{
    [self swizzleInstanceMethod:@selector(description) with:@selector(moneyDescription)];
}

+ (BOOL)swizzleInstanceMethod:(SEL)originalSel with:(SEL)newSel {
    Method originalMethod = class_getInstanceMethod(self, originalSel);
    Method newMethod = class_getInstanceMethod(self, newSel);
    if (!originalMethod || !newMethod) return NO;
    
    class_addMethod(self,
                    originalSel,
                    class_getMethodImplementation(self, originalSel),
                    method_getTypeEncoding(originalMethod));
    class_addMethod(self,
                    newSel,
                    class_getMethodImplementation(self, newSel),
                    method_getTypeEncoding(newMethod));
    
    method_exchangeImplementations(class_getInstanceMethod(self, originalSel),
                                   class_getInstanceMethod(self, newSel));
    return YES;
}







@end

猜你喜欢

转载自blog.csdn.net/u014600626/article/details/84991604