2010-09-15 17 views
25

¿Cómo guardo un NSUInteger usando el protocolo NSCoding, dado que no hay ningún método en NSCoder como -encodeUnsignedInteger:(NSUInteger)anInt forKey:(NSString *)aKey como existe para NSInteger?Cómo almacenar un NSUInteger usando NSCoding?

Lo siguiente funciona, pero ¿es esta la mejor manera de hacerlo? Esto crea innecesariamente objetos.

@interface MYObject : NSObject <NSCoding> { 
    NSUInteger count; 
} 

- (void)encodeWithCoder:(NSCoder *)encoder { 
    [encoder encodeObject:[NSNumber numberWithUnsignedInteger:count] forKey:@"count"]; 
} 

- (id)initWithCoder:(NSCoder *)decoder { 
    self = [super init]; 
    if (self != nil) { 
     count = [[decoder decodeObjectForKey:@"count"] unsignedIntegerValue]; 
    } 
    return self; 
} 
+1

No optimizaría su código para no crear objetos cuando lo escribe por primera vez. Es mejor hacer que algo funcione, que analizar en exceso el código que necesita para escribir. Perderás demasiado tiempo pensando y no tendrás suficiente tiempo para hacerlo. Es conveniente que mida el rendimiento más adelante y realice cambios si la creación del objeto se convierte en un problema. Perfilar en Xcode es una gran herramienta para aprender. –

Respuesta

11

El problema es que NSUInteger puede no tener el mismo tamaño que unsigned int. En muchas (si no en la mayoría de las actuales) máquinas Mac OS X, NSUInteger será un unsigned long, que será el doble de grande. Por otro lado, un archivador con clave debe manejar eso sin problemas, ya que construye un diccionario.

El hecho de que NSCoder no tenga ningún método para tratar con tipos sin firmar es una complicación adicional. No puedo pensar en cómo esto podría causar la pérdida de datos, pero requeriría algunos lanzamientos feos, además de que se siente sucio.

Si desea insistir en un tipo sin signo, la forma más sencilla sería la de codificar los bytes sin formato (preferentemente en el orden de bytes de red mediante htonl y ntohl) en el tipo más grande disponible (unsigned long long) usando encodeBytes:length:forKey: y decodeBytesForKey:returnedLength:. Para una seguridad máxima, debe verificar la longitud de lo que decodificó y lanzar el puntero (o usar una unión) para extraer el tipo de tamaño correcto.

El inconveniente de esto es que el valor se representará en la salida como datos, no como un número entero. Esto solo importa principalmente si alguien decide leer en los datos plist brutos para su archivo en lugar de usar el desarchivar con clave como lo hace, e incluso solo para ellos. Los otros casos en los que podría importar es si Apple (o usted) alguna vez debe cambiar a una arquitectura que tiene incluso tipos enteros más grandes, tipos cuyo tamaño en bits no es una potencia de dos (hay al menos una plataforma antigua donde una palabra era 24 bits), o tipos con un diseño inusual (no grande o poco endian).

En cuanto a su solución NSNumber: Es posible que desee abrir su archivo en el Editor de listas de propiedades y ver qué emitió. Si la salida contiene un elemento entero, entonces es lo mismo que usar encodeInteger:forKey:. Si el resultado contiene un elemento de datos, entonces es el mismo que la solución que mencioné anteriormente. Para ser minucioso, debe verificar el resultado de cada arquitectura que admita.

+0

No * pienso * es necesariamente cierto que si el resultado contiene un número entero, es lo mismo que 'encodeInteger: forKey:'. Los enteros Plist son cadenas de longitud infinita, mientras que 'encodeInteger: forKey:' necesariamente convierte el argumento algo en el rango entero con signo. – Chuck

+0

@Chuck: Acabo de marcar. La solución NSNumber maneja los números que se encuentran arriba de 'NSIntegerMax' correctamente. Un inconveniente, sin embargo: en el modo de 32 bits, leer en un número mayor que el 'NSIntegerMax' de 64 bits lo traerá nuevamente a 0. @Johan Kool: Puede usar' encodeInt64: forKey: '/' decodeInt64ForKey: 'en cambio. –

-1

vas a querer usar

encodeInt:ForKey: 

y

decodeIntForKey: 

métodos de NSCoder. Así, en su caso, lo que se necesita:

- (void)encodeWithCoder:(NSCoder *)encoder { 
    [encoder encodeInt:count forKey:@"count"]; 
} 

- (id)initWithCoder:(NSCoder *)decoder { 
    self = [super init]; 
    if (self != nil) { 
     count = [decoder decodeIntForKey:@"count"]; 
    } 
    return self; 
} 

Esperanza que ayuda, también, hay mucho más codificar métodos "codificar", echar un vistazo a la documentación de NSCoder: D

+0

Conozco ese método (y con la documentación). Mi pregunta es si esto sería seguro, dado que 'int' y' NSUInteger' no son lo mismo. –

+0

@Peter Eso es lo que pensé. ¿Puedo codificar un 'NSUInteger' sin envolverlo en un' NSNumber' o perder información? –

+0

Sí. Técnicamente NSUInteger es simplemente un alias para un entero sin signo, eso es lo que significa la "U". De nuevo, sí, es seguro usar encodeInt: con un NSUInteger. –

2

Esa es la mejor manera de para hacerlo. Parece un trabajo innecesario, pero no hay otro tipo disponible que pueda representar de forma portátil toda la gama de valores que NSUInteger puede contener.

+0

Esta respuesta entra en conflicto directamente con lo que Matt Egan dijo en su respuesta/comentario. Sin embargo, estoy pensando que tienes razón. –

26

NSNumber tiene muchos métodos para almacenar/recuperar tipos de diferentes tamaños y firmados. Es la solución más fácil de usar y no requiere ninguna administración de bytes como sugieren otras respuestas.

Aquí es el tipo de acuerdo con la documentación de Apple en NSNumber:

+ (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value 
- (NSUInteger)unsignedIntegerValue 

Si el ejemplo de código es la mejor manera de codificar/decodificar la NSUInteger. Recomendaría el uso de constantes para los valores clave, para que no los escriba mal y presente errores de archivo.

static NSString * const kCountKey = @"CountKey"; 

@interface MyObject : NSObject <NSCoding> { 
    NSUInteger count; 
} 

@end 

@implementation MyObject 

- (void)encodeWithCoder:(NSCoder *)encoder { 
    [encoder encodeObject:[NSNumber numberWithUnsignedInteger:count] forKey:kCountKey]; 
} 

- (id)initWithCoder:(NSCoder *)decoder { 
    self = [super init]; 
    if (self != nil) { 
     count = [[decoder decodeObjectForKey:kCountKey] unsignedIntegerValue]; 
    } 
    return self; 
} 
@end 
Cuestiones relacionadas