我真的需要将我的项目货币转换为 NSDecimalNumber 吗?

Do I really need to convert my project currency to NSDecimalNumber?

是的,我知道我应该使用 NSDecimalNumber 来处理货币、金钱、价格... I've read this. 问题是,我改编了一个现有的项目,它使用 NSStringNSNumberfloatdoubleCGFloat...)作为货币。他们通过使用 NSNumberFormatter 来处理浮点数,正如我所见,这不是一个大问题(现在?)。这些货币存储在 coredata 中。

现在,如果我想将所有这些货币转换为 NSDecimalNumber,我将不得不对代码进行大量重构并迁移到核心数据中。问题来了:

  1. If (I assume) double, CGFloat, NSNumber can hold the value as large as NSDecimalNumber, why should I use NSDecimalNumber since I can use other with NSNumberFormatter? Is it because of performance?

  2. In case of the necessary of the converting, can I do an auto migration with the help of MappingModel only, of course), or do I have to adapt a custom migration policy?

因为核心数据同时使用 NSStringNSNumber 作为货币,所以请帮我找到一个从这两种数据类型迁移的解决方案。我不习惯在 coredata 中使用 NSDecimalNumber。提前致谢。

编辑

好的,我知道 NSDecimalNumber 是必需的。请帮我回答第二个问题:我可以使用 mappingModel + FUNCTION($source.price, "decimalValue") 之类的东西进行自动迁移吗(这是不正确的,因为 decimalValue return NSDecimal,而不是 NSDecimalNumber)。我真的必须编写自定义迁移策略吗?

问题是 doubleCGFloatNSNumber 中存储的那些值类型无法精确处理小数值。因此,如果您设置 4.50,您最终可能会得到一些近似值。这对于货币价值通常是不可取的,因为您需要精确的表示。您也许可以使用 NSNumberFormatter 进行舍入,但如果您开始使用这些值进行数学计算或需要比百分之一更高的精度,您最终可能会得到不正确的数量。

我怀疑您必须进行重量级迁移才能更改 CoreData 实体的属性类型。即使您不必这样做,您也可能想要这样做,这样您就可以确保转换为 Decimal 的值四舍五入到您选择的精度(因为它们可能是也可能不是您想要的现在的数据库)。

听起来您的项目中有一些技术债务。您是选择现在偿还债务还是等到债务增长,最终取决于您。

您可能想使用 NSDecimalNumber,而不是 NSDecimalNSDecimalstruct,而 NSDecimalNumber 是继承自 NSNumber 的 Objective-C class。因此,您通常可以在接受 NSNumber 的任何地方传递 NSDecimalNumberNSNumberFormatter 自动格式化 NSDecimalNumber 个实例,如果您将格式化程序的 generatesDecimalNumbers 设置为 YES,它会将字符串解析为 NSDecimalNumber 个实例。

NSDecimalNSDecimalNumber的范围大于doubleCGFloat。十进制类型存储更多有效数字并具有更大的指数范围。

我不知道自动迁移问题的答案,但我知道 Core Data 支持 NSDecimalNumber。您可以在模型中将属性的类型设置为十进制 (NSDecimalAttributeType)。

Do I really have to write a custom migration policy?

是的。为每个需要转换的实体创建一个 NSEntityMigrationPolicy 的 subclass 并实现 menthod createDestinationInstancesForSourceInstance:entityMapping:manager:error:。在映射模型中实体的自定义策略中输入此 class 的名称。

- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)sInstance
    entityMapping:(NSEntityMapping *)mapping manager:(NSMigrationManager *)manager
    error:(NSError **)error
{
    BOOL aBool = [super createDestinationInstancesForSourceInstance:sInstance entityMapping:mapping manager:manager error:error];
    if (aBool)
    {
        NSArray *aSourceArray = [[NSArray alloc] initWithObjects:&sInstance count:1];
        NSArray *aDestArray = [manager destinationInstancesForEntityMappingNamed:[mapping name] sourceInstances:aSourceArray];
        if (aDestArray && [aDestArray count] > 0)
        {
            NSManagedObject *dInstance = [aDestArray objectAtIndex:0];
            // conversion
        }
    }
    return aBool;
}