2009-11-09 16 views
26

He leído mucho sobre NSDecimal, NSNumber, NSNumberDecimal, CFNumber ... y comienza a ser una especie de jungla para mí.¿Cuál es la elección correcta entre NSDecimal, NSDecimalNumber, CFNumber?

Básicamente, estoy tratando de crear una sencilla clase de modelo que se encargará de cálculos simples, como éste:

#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 

que desee acceder a estos métodos y definidores con sus nombres como cadenas, ya que planeo tener muchas otras clases como esta, y solo estoy manteniendo una lista de campos para hacer la codificación de valor clave.

// 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")]; 

entiendo que esto no es posible, que performSelector: volverá un objeto, y por lo tanto que capitalizedAmount debe devolver un objeto. He leído cosas sobre NSInvocation y la parte correspondiente en Objective-C Faq en comp.lang.

también entiendo que debo utilizar NSDecimalNumber, pero hay dos cosas que me gustaría saber:

  1. son la sobrecarga de memoria y pérdida de rendimiento aceptable para una clase algo más complicado (sólo cálculos financieros de este tipo, se muestra en una UITableView)? No tengo mucha experiencia en C ...
  2. ¿No es demasiado fastidioso y complicado utilizar funciones como decimalNumberByAdding:? Con Python fue fácil definir __add__ para usar operadores con objetos. ¿Debo obtener valores flotantes de NSDecimalNumber, luego hacer los cálculos y devolver el resultado envuelto en un NSDecimalNumber? ¿Cómo lidiarías con este problema?

¡Estoy buscando una solución simple y hermosa!

Otra pregunta más en la misma área: ¿es CFBoolean el contenedor de objetos para BOOL en iPhone Core Foundation?

¡Muchas gracias por su ayuda!

Respuesta

24

Si se trata de cálculos financieros, realmente debe utilizar la aritmética de base 10 para evitar los errores de redondeo que pueden ocurrir con los tipos de punto flotante base-2 estándar. Entonces es NSDecimal o NSDecimalNumber. Y dado que está escribiendo código orientado a objetos, NSDecimalNumber es la elección correcta para usted.

Para responder a sus preguntas: solo las pruebas de su código pueden revelar si la sobrecarga de memoria y la pérdida de rendimiento son aceptables para usted. Realmente no he trabajado mucho con NSDecimalNumber, pero apostaría a que la implementación de Apple es bastante eficiente y será más que adecuada para las necesidades de la mayoría de las personas.

Desafortunadamente, no podrá evitar los gustos de decimalNumberByAdding: ya que Objective-C no admite la sobrecarga del operador como hace C++. Estoy de acuerdo en que hace que tu código sea menos elegante.

Un comentario sobre el código que ha publicado: r = [obj performSelector:NSSelectorFromString(@"capitalizedAmount")]; es bastante poco elegante.De cualquier

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

o incluso el simple

r = [obj capitalizedAmount]; 

sería mejor a menos que requiera la sintaxis NSSelectorFromString por alguna otra razón.

+0

Gracias por su respuesta, efectivamente necesito crear selectores de cadena. ¿Es el CFBoolean el objeto correcto para envolver BOOL? – charlax

+1

En Cocoa, debe usar NSNumber para ajustar un BOOL en un objeto. Ver '+ [NSNumber numberWithBool:]'. –

2

Quiero acceder a estos métodos y setters con sus nombres como cadenas, ya que planeo tener muchas otras clases como esta, y solo estoy manteniendo una lista de campos para hacer la codificación del valor clave.

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

KVC no es simplemente enviando mensajes a los objetos utilizando cuerdas. Ver the Key-Value Coding Programming Guide.

¿Debo obtener los valores flotantes de NSDecimalNumber, luego hacer los cálculos y devolver el resultado envuelto en un NSDecimalNumber?

No. conversión a binario de punto flotante (float/double) de decimal de coma flotante (NSDecimal/NSDecimalNumber) es con pérdida. Su resultado será incorrecto para algunos cálculos si los hace de esa manera.

19

Ole es correcto, en el sentido de que debe utilizar NSDecimal o NSDecimalNumber para evitar los errores matemáticos de punto flotante al hacer cálculos financieros. Sin embargo, mi sugerencia sería usar NSDecimal y sus funciones C, en lugar de NSDecimalNumber. Los cálculos NSDecimal pueden ser mucho más rápidos, y ya que evitan crear muchos objetos liberados automáticamente, mucho mejor en el uso de la memoria.

A modo de ejemplo, que las operaciones matemáticas de punto de referencia para los dos tipos en mi 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 

divisiones eran la única operación que no experimentaron un aumento más o menos de cinco veces en el rendimiento al utilizar NSDecimal vs NSDecimalNumber . Se producen mejoras de rendimiento similares en el iPhone. Esto, junto con el ahorro de memoria, fue la razón por la que recientemente cambiamos el Core Plot al uso de NSDecimal.

La única dificultad con la que se encontrará es para obtener valores dentro y fuera de los tipos de NSDecimal. Yendo directamente ay desde valores flotantes y enteros podría requerir el uso de NSDecimalNumber como un puente. Además, si usa Core Data, almacenará sus valores como NSDecimalNumbers, no como NSDecimals.

+0

Gracias Brad por su punto de referencia, que ya había visto y me pareció muy interesante. Planeo usar NSDecimalNumber porque los encontré más fáciles de usar, y usaré Core Data en el futuro cercano. Por cierto, ¡no necesito tanta velocidad como tu biblioteca gráfica! – charlax

+0

¿Cuál es la forma más sencilla de crear un NSDecimal desde un flotante o doble? – Paul

+0

@Paul: consulte el archivo CPUtilities.m del marco de Core Plot para ver algunas funciones de ayuda que realizan varias conversiones: http://code.google.com/p/core-plot/source/browse/framework/Source/CPUtilities.m . Personalmente, me he dedicado a crear instancias NSDecimalNumber utilizando cadenas de formato y luego obtener el 'decimalValue' de ellas, porque soy un poco paranoico acerca de la conversión de doble a NSNumber a NSDecimal que ocurre en las rutinas de Core Plot. –

Cuestiones relacionadas