iphone - ¿Cuál es la elección correcta entre NSDecimal, NSDecimalNumber, CFNumber?
objective-c cocoa (3)
He leído mucho sobre NSDecimal, NSNumber, NSNumberDecimal, CFNumber ... y comienza a ser una especie de jungla para mí.
Básicamente, estoy tratando de crear una clase de modelo simple que manejará cálculos simples, como este:
#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
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 de valores 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:
devolverá un objeto, y por lo tanto capitalizedAmount
debería devolver un objeto. He leído cosas sobre NSInvocation
y la parte relevante en Objective-C Faq en comp.lang.
También entiendo que debería usar NSDecimalNumber
, pero hay dos cosas que me gustaría saber:
- ¿La sobrecarga de memoria y la pérdida de rendimiento son aceptables para una clase algo más complicada (solo se muestran cálculos financieros de este tipo en una UITableView)? No tengo mucha experiencia en C ...
- ¿No es demasiado fastidioso y complicado usar funciones como
decimalNumberByAdding:
:? Con Python fue fácil definir__add__
para usar operadores con objetos. ¿Debo obtener valores float deNSDecimalNumber
, luego hacer los cálculos y devolver el resultado envuelto en unNSDecimalNumber
? ¿Cómo lidiarías con este problema?
Estoy buscando una solución simple y hermosa!
Solo otra pregunta en la misma área: ¿es CFBoolean
el contenedor de objetos para BOOL
en iPhone Core Foundation?
¡Muchas gracias por su ayuda!
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 de valores clave.
r = [obj performSelector:NSSelectorFromString(@"capitalizedAmount")];
KVC no está simplemente enviando mensajes a objetos usando cadenas. Consulte la Guía de programación de codificación de clave-valor .
¿Debo obtener valores float de NSDecimalNumber, luego hacer los cálculos y devolver el resultado envuelto en un NSDecimalNumber?
No. La conversión a punto flotante binario ( float
/ double
) a partir de coma flotante decimal ( NSDecimal
/ NSDecimalNumber) es con pérdida. Su resultado será incorrecto para algunos cálculos si los hace de esa manera.
Ole es correcto, en el sentido de que debe usar NSDecimal o NSDecimalNumber para evitar los errores matemáticos de coma 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.
Como ejemplo, comparé las operaciones matemáticas 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
Las divisiones fueron la única operación que no experimentó un aumento de rendimiento de aproximadamente cinco veces al usar 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 al ingresar y quitar valores de los tipos 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.
Si está tratando con cálculos financieros, realmente debería usar 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.
Lamentablemente, no podrá evitar los gustos de decimalNumberByAdding:
dado que Objective-C no admite la sobrecarga del operador como lo 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. Ya sea
r = [obj performSelector:@selector(capitalizedAmount)];
o incluso el simple
r = [obj capitalizedAmount];
Sería mejor a menos que requiera la sintaxis NSSelectorFromString
por algún otro motivo.