NSDecimal,NSDecimalNumber,CFNumber之间的正确选择是什么?

时间:2009-11-09 22:50:22

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

我已经阅读了很多关于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

我想以名字作为字符串来访问这些方法和setter,因为我打算有很多其他类这样的类,我只保留一个字段列表来进行键值编码。

// 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应该返回一个对象。我已经阅读了关于NSInvocation的内容以及关于comp.lang的Objective-C Faq中的相关部分。

我也理解我应该使用NSDecimalNumber,但我想知道两件事:

  1. 内存开销和性能损失是否可以接受更复杂的类(仅在UITableView中显示此类财务计算)?我没有太多的C背景......
  2. 使用像decimalNumberByAdding:这样的功能不是太过于挑剔和复杂吗?使用Python,可以很容易地定义__add__以将运算符与对象一起使用。我应该从NSDecimalNumber获取浮点值,然后进行计算并返回包含在NSDecimalNumber中的结果吗?你会如何处理这个问题?
  3. 我正在寻找一个简单而美观的解决方案!

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

    非常感谢你的帮助!

3 个答案:

答案 0 :(得分:26)

如果您正在处理财务计算,那么您确实应该使用base-10算法来避免标准base-2浮点类型可能出现的舍入错误。所以它是NSDecimal或NSDecimalNumber。由于您正在编写面向对象的代码,因此NSDecimalNumber是您的正确选择。

回答您的问题:只测试您的代码可以揭示您是否可以接受内存开销和性能损失。我并没有真正使用过NSDecimalNumber,但是我敢打赌,Apple的实施非常有效,并且足以满足大多数人的需求。

不幸的是,由于Objective-C不像C ++那样支持运算符重载,因此您将无法避免使用decimalNumberByAdding:之类的内容。我同意它会使你的代码不那么优雅。

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

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

甚至是简单的

r = [obj capitalizedAmount];
除非您出于某种其他原因需要NSSelectorFromString语法,否则

会更好。

答案 1 :(得分:19)

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类型。直接进出float和integer值可能需要使用NSDecimalNumber作为桥。此外,如果您使用Core Data,您将把值存储为NSDecimalNumbers,而不是NSDecimals。

答案 2 :(得分:3)

  

我想以名字作为字符串来访问这些方法和setter,因为我打算有很多其他类这样的类,我只保留一个字段列表来进行键值编码。

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

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

  

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

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