Подтвердить что ты не робот

Каков правильный выбор между NSDecimal, NSDecimalNumber, CFNumber?

Я много читал о 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 должен вернуть объект. Я читал о NSInvocation и соответствующей части в Objective-C Faq на comp.lang.

Я также понимаю, что я должен использовать NSDecimalNumber, но есть две вещи, которые я хотел бы знать:

  • Являются ли служебные данные памяти и потери производительности приемлемыми для более сложного класса (только финансовые вычисления такого рода, показанные в UITableView)? У меня мало опыта в C...
  • Разве это не слишком изящно и сложно использовать такие функции, как decimalNumberByAdding:? С Python было легко определить __add__ для использования операторов с объектами. Должен ли я получать значения float из NSDecimalNumber, затем выполнять вычисления и возвращает результат, завернутый в NSDecimalNumber? Как бы вы справились с этой проблемой?

Я ищу простое и красивое решение!

Еще один вопрос в той же области: CFBoolean обертка объекта для BOOL на iPhone Core Foundation?

Большое спасибо за вашу помощь!

4b9b3361

Ответ 1

Если вы имеете дело с финансовыми расчетами, вам действительно нужно использовать арифметику base-10, чтобы избежать ошибок округления, которые могут возникать со стандартными типами с плавающей запятой base-2. Таким образом, это либо NSDecimal, либо NSDecimalNumber. И поскольку вы пишете объектно-ориентированный код, NSDecimalNumber является правильным выбором для вас.

Чтобы ответить на ваши вопросы: только тестирование вашего кода может показать, приемлемы ли накладные расходы на память и потеря производительности. Я не очень много работал с NSDecimalNumber, но я бы сказал, что реализация Apple довольно эффективна и будет более чем достаточной для большинства людей.

К сожалению, вы не сможете избежать подобных decimalNumberByAdding:, поскольку Objective-C не поддерживает перегрузку оператора, как это делает С++. Я согласен, что это делает ваш код несколько менее элегантным.

Один комментарий к опубликованному вами коду: r = [obj performSelector:NSSelectorFromString(@"capitalizedAmount")]; довольно нечеткий. Либо

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

или даже простой

r = [obj capitalizedAmount];

будет лучше, если вам не понадобится синтаксис NSSelectorFromString по какой-либо другой причине.

Ответ 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.

Ответ 3

Я хочу получить доступ к этим методам и сеттерам с их именами как строки, так как я планирую иметь много других классов, подобных этому, и я сохраняю только список полей для кодирования ключевых значений.

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

KVC не просто отправляет сообщения объектам с помощью строк. См. Руководство по программированию кодирования по значению ключа.

Должен ли я получать значения float из NSDecimalNumber, затем выполнять вычисления и возвращает результат, завернутый в NSDecimalNumber?

Нет. Преобразование в двоичную с плавающей запятой (float/double) из десятичной с плавающей запятой (NSDecimal/NSDecimalNumber) является убыточным. Ваш результат будет некорректным для некоторых вычислений, если вы сделаете их таким образом.