Каков правильный выбор между NSDecimal, NSDecimalNumber, CFNumber? - PullRequest
27 голосов
/ 10 ноября 2009

Я много читал о 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 и соответствующей части в FAQ по Objective-C на comp.lang.

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

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

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

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

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

Ответы [ 3 ]

26 голосов
/ 10 ноября 2009

Если вы имеете дело с финансовыми вычислениями, вам действительно следует использовать арифметику с базой 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.

19 голосов
/ 10 ноября 2009

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 и целым числам и от них может потребоваться использование NSDecimalNumber в качестве моста. Кроме того, если вы используете базовые данные, вы будете хранить свои значения как NSDecimalNumbers, а не NSDecimals.

3 голосов
/ 10 ноября 2009

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

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

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

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

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

...