【问题标题】:What is the right choice between NSDecimal, NSDecimalNumber, CFNumber?NSDecimal、NSDecimalNumber、CFNumber 之间的正确选择是什么?
【发布时间】:2009-11-09 22:50:21
【问题描述】:

我已经阅读了很多关于 NSDecimal、NSNumber、NSNumberDecimal、CFNumber... 的内容,但它开始对我来说是一种丛林。

基本上,我正在尝试创建一个简单的模型类来处理简单的计算,比如这个:

#import <Foundation/Foundation.h>


@interface Test : NSObject
{
    float rate;
    float amount;
    int duration;
}

- (float)capitalizedAmount;

@end

@implementation Test

- (float)capitalizedAmount {
    return (amount*pow((1.0+rate),duration));
}

@end

我想用它们的名字作为字符串来访问这些方法和设置器,因为我计划有很多像这个这样的其他类,我只保留一个字段列表来进行键值编码。

// This is just the desired behavior
// This evidently won't work with the previous class definition
Test *obj = [[Test alloc] init];
[NSNumber numberWithInt:10]
...
float r;
r = [obj performSelector:NSSelectorFromString(@"capitalizedAmount")];

我知道这是不可能的,performSelector: 将返回一个对象,因此capitalizedAmount 应该返回一个对象。我在 comp.lang 上的 Objective-C 常见问题解答中阅读了有关 NSInvocation 和相关部分的内容。

我也明白我应该使用NSDecimalNumber,但有两件事我想知道:

  1. 对于更复杂的类(只有这种财务计算,在 UITableView 中显示)是否可以接受内存开销和性能损失?我没有太多的 C 语言背景...
  2. 使用decimalNumberByAdding:这样的函数是不是太挑剔和复杂了?使用 Python 很容易定义 __add__ 以将运算符与对象一起使用。我是否应该从NSDecimalNumber 获取浮点值,然后进行计算并返回包装在NSDecimalNumber 中的结果?您将如何处理这个问题?

我正在寻找一个简单而漂亮的解决方案!

同一领域的另一个问题:CFBoolean 是 iPhone Core Foundation 上BOOL 的对象包装器吗?

非常感谢您的帮助!

【问题讨论】:

    标签: iphone objective-c cocoa key-value-coding


    【解决方案1】:

    如果您正在处理金融计算,您确实应该使用以 10 为底的算术来避免标准以 2 为底的浮点类型可能发生的舍入错误。所以它是 NSDecimal 或 NSDecimalNumber。由于您正在编写面向对象的代码,因此 NSDecimalNumber 是您的正确选择。

    回答您的问题:只有测试您的代码才能揭示您是否可以接受内存开销和性能损失。我在 NSDecimalNumber 方面的工作并不多,但我敢打赌 Apple 的实现非常高效,足以满足大多数人的需求。

    不幸的是,您将无法避免 decimalNumberByAdding: 之类的问题,因为 Objective-C 不像 C++ 那样支持运算符重载。我同意这会使您的代码不那么优雅。

    对您发布的代码的一条评论:r = [obj performSelector:NSSelectorFromString(@"capitalizedAmount")]; 相当不雅。要么

    r = [obj performSelector:@selector(capitalizedAmount)];
    

    甚至是简单的

    r = [obj capitalizedAmount];
    

    会更好,除非您出于其他原因需要 NSSelectorFromString 语法。

    【讨论】:

    • 感谢您的回答,我实际上需要从字符串创建选择器。 CFBolean 是包装 BOOL 的正确对象吗?
    • 在 Cocoa 中,您应该使用 NSNumber 将 BOOL 包装在对象中。见+[NSNumber numberWithBool:]
    【解决方案2】:

    Ole 是正确的,因为您应该使用 NSDecimal 或 NSDecimalNumber 来避免在进行财务计算时出现浮点数学错误。但是,我的建议是使用 NSDecimal 及其 C 函数,而不是 NSDecimalNumber。 NSDecimal 计算可以更快,并且由于它们避免创建大量自动释放的对象,因此在内存使用方面要好得多。

    例如,我在 MacBook Air 上对这两种类型的数学运算进行了基准测试:

    NSDecimal
    
    Additions per second: 3355476.75
    Subtractions per second: 3866671.27
    Multiplications per second: 3458770.51
    Divisions per second: 276242.32
    
    NSDecimalNumber
    
    Additions per second: 676901.32
    Subtractions per second: 671474.6
    Multiplications per second: 720310.63
    Divisions per second: 190249.33
    

    在使用 NSDecimal 与 NSDecimalNumber 时,除法运算是唯一没有将性能提高大约五倍的运算。 iPhone 上也有类似的性能改进。这与节省的内存一起,是我们最近将 Core Plot 切换到使用 NSDecimal 的原因。

    您会遇到的唯一困难是将值传入和传出 NSDecimal 类型。直接往返浮点和整数值可能需要使用 NSDecimalNumber 作为桥梁。此外,如果您使用 Core Data,您会将值存储为 NSDecimalNumbers,而不是 NSDecimals。

    【讨论】:

    • 感谢 Brad 的基准测试,我已经看过并觉得非常有趣。我计划使用 NSDecimalNumber,因为我发现它们更易于使用,并且我将在不久的将来使用 Core Data。顺便说一句,我不需要你的图形库那么快!
    • 从浮点数或双精度数创建 NSDecimal 的最简单方法是什么?
    • @Paul - 查看 Core Plot 框架的 CPUtilities.m 文件,了解一些可以进行各种转换的辅助函数:code.google.com/p/core-plot/source/browse/framework/Source/…。就个人而言,我已经开始使用格式字符串创建 NSDecimalNumber 实例,然后从中获取 decimalValue,因为我对 Core Plot 例程中进行的从 double 到 NSNumber 到 NSDecimal 的转换有点偏执。跨度>
    • 请记住,如果您想存储这些数据并使用 Core Data,尽管性能差异,NSDecimalNumber 是正确的选择。
    【解决方案3】:

    我想用它们的名字作为字符串来访问这些方法和设置器,因为我计划有很多像这样的其他类,我只保留一个字段列表来进行键值编码。

    r = [obj performSelector:NSSelectorFromString(@"capitalizedAmount")];
    

    KVC 不仅仅是使用字符串向对象发送消息。见the Key-Value Coding Programming Guide

    我是否应该从 NSDecimalNumber 获取浮点值,然后进行计算并返回包装在 NSDecimalNumber 中的结果?

    没有。从十进制浮点 (NSDecimal/NSDecimalNumber) 转换为二进制浮点 (float/double) 是有损的。如果你这样做,你的结果对于某些计算是不正确的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-05
      • 2022-11-13
      • 1970-01-01
      • 1970-01-01
      • 2014-04-08
      相关资源
      最近更新 更多