2011-10-06 16 views
6

Algo raro sucedió.NSNumber almacenado en NSUserDefaults

He almacenado un NSNumber con un valor largo sin signo en NSUserDefaults. Cuando lo recupero, el valor acaba de cambiar. Parece que el sistema cree que el número es largo en lugar de largo.

Lo que es peor es que cuando comparo el número recuperado de UserDefaults con el número original, el resultado es NotEqual!

¿Qué pasa con el código? ¡Gracias!

static NSString * const NumberKey = @"MyNumber"; 
unsigned long long value = 15908045869032883218ULL; 
if ([[NSUserDefaults standardUserDefaults] objectForKey:NumberKey] == nil) { 

    NSNumber *number = [NSNumber numberWithUnsignedLongLong:value]; 
    [[NSUserDefaults standardUserDefaults] setObject:number forKey:NumberKey]; 
    NSLog(@"Original Number:%@", number); // 15908045869032883218, right 
} 

NSNumber *number = [[NSUserDefaults standardUserDefaults] objectForKey:NumberKey]; 
NSLog(@"Current Number:%@", number); // -2538698204676668398, weird 
NSLog(@"Current Value:%llu", [number unsignedLongLongValue]); // 15908045869032883218, right 
NSLog(@"%d", [number isEqualToNumber:[NSNumber numberWithUnsignedLongLong:value]]); // 0 
NSLog(@"%d", [number unsignedLongLongValue] == value); // 1 

Respuesta

6

Para responder mejor a su pregunta. Si nos fijamos en isEqualToNumber la documentation for NSNumber 's: La función se dará cuenta de la siguiente línea,

objetos

Dos NSNumber se consideran iguales si tienen los mismos Identificación valores o si tienen valores equivalentes

es importante que comprenda esto. En su código que está preguntando es mi número NSNumber "número" igual a "valor", no está preguntando ¿el valor numérico almacenado en mi objeto NSNumber "número" es igual al valor numérico almacenado en mi objeto NSNumber "valor".

La última línea de código que ha escrito muestra que, de hecho, los valores numéricos de su NSNumber son, de hecho, iguales.

NSLog(@"%d", [number unsignedLongLongValue] == value); //1 

Así que el almacenamiento y la recuperación de los valores correctamente, usted debe utilizar el método de comparación == con objetos NSNumber almacena valores numéricos (es decir intValue == intValue, unsignedLongLongValue == unsignedLongLongValue) y no la comparación de sus objetos de id juntos.

En cuanto a esta línea de código

NSLog(@"Current Number:%@", number); // -2538698204676668398, weird 

Esto no es raro, esto es perfectamente normal, como usted ha dicho NSLog para imprimir una representación NSObject de 'número'. No estoy 100% seguro pero creo que la función - (NSString *) description de NSNumber tiene como valor predeterminado devolver un valor int sin signo para el valor numérico que contiene. Es por eso que está obteniendo el gran número negativo devuelto. Es posible que desee ver en función de NSNumber - (NSString *)descriptionWithLocale:(id)aLocale a imprimir los datos de una forma más lógica para para usted, o usted podría utilizar

NSLog(@"Current Number:%llu", [number unsignedLongLongValue]); 

que le dará la respuesta correcta.

EDIT:

Además de esto, después de mirar en la edición de lo que está sucediendo es que en el recuerdo de su objeto NSNumber de UserDefaults es el tipo de número original no está siendo preservado (esta información se pone de relieve en la documentación para NSNumber en la sección de información general)

(Tenga en cuenta que los objetos numéricos no necesariamente conservan el tipo que se crean con.)

Se puede ver esto por sí mismo si se registra el siguiente después de recuperar el "número" de valores predeterminados del usuario (añadir esto al final del código que tiene en su pregunta) y echar un vistazo a los valores de codificación que se muestran here

NSLog(@"%s", [number objCType]); //This will log out q 
NSLog(@"%s", [[NSNumber numberWithUnsignedLongLong:value] objCType]); //this will log out Q 

La diferencia entre Q y Q es que Q es un valor sin signo ... de ahí que tenga problemas con la función isEqualToNumber: los tipos de números son diferentes. Si está tan inactivo al usar la función iSEqualToNumber: para comparar valores, entonces podría implementar esto para recuperar su valor de NSUserDefaults.

NSNumber *number = [NSNumber numberWithUnsignedLongLong:[[NSUserDefaults standardUserDefaults] objectForKey:NumberKey] unsignedLongLongValue]]; 

Se podría contemplar la utilización de la NSNumber comparar: función para ver si el valor devuelto es NSOrderedSame sin embargo, esto no va a funcionar para comparar sin firmar vs valores con signo del mismo tipo por lo que en su situación que haría uso de los anteriores como recuperar los datos de NSUserDefaults está eliminando la "firma" de su número.

+0

Estoy de acuerdo con usted. Sin embargo, "Dos objetos NSNumber se consideran iguales si tienen los mismos valores de id o si tienen valores equivalentes" significa que puedo depender del isEqualToNumber: para determinar el valor de dos objetos NSNumber, pero el resultado de mi código revela que no puede depender de este método Tengo que deducir por mí mismo que el tipo de valor, y usar el método "XXValue" y "==" para comparar dos objetos NSNumber, lo cual no es conveniente, porque el método isqual: se usa en muchos otros objetos como NSArray, NSDictionary. – Swordsfrog

+0

@Swordsfrog después de analizar esto un poco más la naturaleza de sus problemas también se relaciona con una respuesta más adelante. Editaré mi respuesta original para demostrar esto también. –

+0

Gracias, eso acaba de resolver mi problema. – Swordsfrog

1

Se almacenarla correctamente, no hay nada malo con su código, excepto:

NSLog(@"Current Number:%@", number);

Aquí number es un objeto no sea una cadena, se podría pensar en él como un envoltorio para un primitivo numérica . O puede pensar que las instancias NSNumber objetifican un tipo primitivo.

Lo que se necesita es algo como:

NSLog(@"Current Number:%@", [number stringValue]);

+0

Lo que quiero saber es por qué el número recuperado de NSUserDefaults no es igual al número original. – Swordsfrog

0

Aquí es una respuesta especulativa:

La documentación NSNumber establece que:

(Tenga en cuenta que los objetos numéricos no lo hacen necesariamente preservar el tipo con el que se crean).

Debe estar utilizando un almacenamiento interno diferente para este tipo, y solo le proporciona el valor correcto cuando solicita específicamente el valor largo sin signo. El método description, que se llama en su instrucción NSLog, puede estar predeterminado en un tipo de representación diferente.

Y/o puede haber alguna peculiaridad del desarchivo que impide que el método isEqualToNumber trabaje en el valor devuelto por defecto. Si hace esa comparación entre dos NSNumbers creados en el mismo ámbito, ¿funciona? El valor correcto definitivamente está ahí en alguna parte dado que su última declaración devuelve verdadera.

+0

estaba en el camino correcto con esta respuesta. Bien hecho. –

3

Al final del día, si se desea almacenar NSNumber en NSUserDefaults este código funciona para mí, incluso para grandes números enteros como: 881217446193276338

Para guardar:

[[NSUserDefaults standardUserDefaults] setObject:self.myUser.sessionid forKey:@"sessionid"]; 
[[NSUserDefaults standardUserDefaults] synchronize]; 

Para recuperar:

self.myUser.sessionid = (NSNumber *)[[NSUserDefaults standardUserDefaults] objectForKey:@"sessionid"]; 
+2

Me alegro de haber escrito esta respuesta, porque 6 meses después me encontré con el mismo problema, lo busqué en Google y llegué aquí y vi mi propia respuesta y nuevamente resolvió mi problema :) – Ali