2009-11-09 2 views
26

Я много читал о NSDecimal, NSNumber, NSNumberDecimal, CFNumber ... и он стал для меня своего рода джунглями.Каков правильный выбор между NSDecimal, NSDecimalNumber, 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, но есть две вещи, которые я хотел бы знать:

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

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

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

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

ответ

24

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

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

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

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

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

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

r = [obj capitalizedAmount]; 

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

+0

Спасибо за ваш ответ, мне действительно нужно создать селектор из строки. Является ли CFBoolean правильным объектом для упаковки BOOL? – charlax

+1

В какао вы должны использовать NSNumber для обертывания BOOL в объекте. См. '+ [Номер NSNumberWithBool:]'. –

2

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

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

КВЦ не просто отправка сообщений на объекты с помощью строки. См. the Key-Value Coding Programming Guide.

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

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

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

+0

Спасибо, Брэд за ваш бенчмарк, который я уже видел и нашел очень интересным. Я планирую использовать NSDecimalNumber, поскольку я нашел их более удобными в использовании, и я буду использовать Core Data в ближайшем будущем. Кстати, мне не нужна такая скорость, как ваша графическая библиотека! – charlax

+0

Каков самый простой способ создать NSDecimal из поплавка или двойного? – Paul

+0

@Paul. Посмотрите на файл CPUtilities.m фреймворка Core Plot для некоторых вспомогательных функций, которые выполняют различные преобразования: http://code.google.com/p/core-plot/source/browse/framework/Source/CPUtilities.m. , Лично я начал создавать экземпляры NSDecimalNumber с использованием строк формата, а затем получал от них 'decimalValue', потому что я немного параноик относительно преобразования из double в NSNumber в NSDecimal, которое продолжается в подпрограммах Core Plot. –

Смежные вопросы