2012-02-24 17 views
13

Estoy usando KVC para serializar un NSObject e intento guardarlo en NSUserDefaults, lo cual me da un Attempt to insert non-property value cuando intento almacenar mi NSDictionary.¿Por qué NSUserDefaults no está dispuesto a guardar mi NSDictionary?

A continuación se presentan las propiedades del objeto en cuestión, MyClass:

@interface MyClass : NSObject 
@property (copy,nonatomic) NSNumber* value1; 
@property (copy,nonatomic) NSNumber* value2; 
@property (copy,nonatomic) NSString* value3; 
@property (copy,nonatomic) NSString* value4; 
@property (copy,nonatomic) NSString* value5; 
@property (copy,nonatomic) NSString* value6; 
@property (copy,nonatomic) NSString* value7; 
@property (copy,nonatomic) NSString* value8; 
@end 

Cuando es hora de salvar MiClase Ocurre aquí:

-(void)saveMyClass 
{ 
    NSArray* keys = [NSArray arrayWithObjects: 
       @"value1", 
       @"value2", 
       @"value3", 
       @"value4", 
       @"value5", 
       @"value6", 
       @"value7", 
       @"value8", 
       nil]; 
    NSDictionary* dict = [self dictionaryWithValuesForKeys:keys]; 
    for(id key in [dict allKeys]) 
    { 
    NSLog(@"%@ %@",[key class],[[dict objectForKey:key] class]); 
    } 
    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; 
    [defaults setObject:dict forKey:[NSString stringWithString:kMyClassKey]]; 
    [defaults synchronize]; 
} 

que produce esta salida:

2012-02-23 19:35:27.518 MyApp[10230:40b] __NSCFConstantString __NSCFNumber 
2012-02-23 19:35:27.519 MyApp[10230:40b] __NSCFConstantString __NSCFNumber 
2012-02-23 19:35:27.519 MyApp[10230:40b] __NSCFConstantString __NSCFString 
2012-02-23 19:35:27.519 MyApp[10230:40b] __NSCFConstantString __NSCFString 
2012-02-23 19:35:27.520 MyApp[10230:40b] __NSCFConstantString __NSCFString 
2012-02-23 19:35:27.520 MyApp[10230:40b] __NSCFConstantString __NSCFString 
2012-02-23 19:35:27.520 MyApp[10230:40b] __NSCFConstantString __NSCFString 
2012-02-23 19:35:27.520 MyApp[10230:40b] __NSCFConstantString NSNull 
2012-02-23 18:38:48.489 MyApp[9709:40b] *** -[NSUserDefaults setObject:forKey:]: Attempt to insert non-property value '{ 
    value1 = "http://www.google.com"; 
    value2 = "MyClassData"; 
    value3 = 8; 
    value4 = "<null>"; 
    value5 = "http://www.google.com"; 
    value6 = 1; 
    value7 = "http://www.google.com"; 
    value8 = 4SY8KcTSGeKuKs7s; 
}' of class '__NSCFDictionary'. Note that dictionaries and arrays in property lists must also contain only property values.` 

Como puede ver, todos los objetos en el dict son pro Los valores de la lista perty y todas sus claves son NSString*. ¿Qué curiosidades me faltan para ejecutar esto? ¿O debería renunciar y usar writeToFile o similar?

+1

Simplemente está imprimiendo las claves. ¿Y los valores? –

+0

Eso es todo, uno de mis valores se coló como 'NSNull' (JSON' null' producido por 'JSONKit') y esa es casi definitivamente la causa. No he trabajado una solución todavía. –

Respuesta

20

Apoyos a Kevin que sugirió imprimir los valores, por supuesto uno de los cuales es del tipo NSNull que no es un valor de lista de propiedades. ¡Gracias!

La solución kludgy a mi problema - iterar sobre las teclas del diccionario que acabo de producir tan cómodamente con dictionaryWithValuesForKeys y eliminar las del tipo NSNull. suspiro

NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithDictionary:[self dictionaryWithValuesForKeys:keys]]; 
for(id key in [dict allKeys]) 
{ 
    if([[dict valueForKey:key] isKindOfClass:[NSNull class]]) 
    { 
     // doesn't work - values that are entered will never be removed from NSUserDefaults 
     //[dict removeObjectForKey:key]; 
     [dict [email protected]"" forKey:key]; 
    } 
} 
+6

Btw ¡Te odio 'JSONKit' y tu! @ #% @ #! 'NSNull's –

+1

Decidí que' [dict removeObjectForKey: clave]; 'es en realidad una solución insuficiente para mi problema, porque los valores perdidos nunca se sobrescribirán en' NSUserDefaults'. En cambio, ahora estoy guardando una cadena vacía cuando se detecta un 'NSNull', como se refleja en la respuesta. –

+0

¡genial! : D Esta solución funciona para mí. Gracias –

18

Normalmente archive y descomprimo diccionarios al guardarlos en los valores predeterminados del usuario. De esta forma no tiene que verificar manualmente los valores NSNull.

Simplemente agregue los siguientes dos métodos a su código. Potencialmente en una categoría.

- (BOOL)archive:(NSDictionary *)dict withKey:(NSString *)key { 
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 
    NSData *data = nil; 
    if (dict) { 
     data = [NSKeyedArchiver archivedDataWithRootObject:dict]; 
    } 
    [defaults setObject:data forKey:key]; 
    return [defaults synchronize]; 
} 

- (NSDictionary *)unarchiveForKey:(NSString *)key { 
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 
    NSData *data = [defaults objectForKey:key]; 
    NSDictionary *userDict = nil; 
    if (data) { 
     userDict = [NSKeyedUnarchiver unarchiveObjectWithData:data]; 
    } 
    return userDict; 
} 

A continuación, puede archivar cualquier diccionario como este (suponiendo que el método están disponibles en la clase):

NSDictionary *dict = ...; 
[self archive:dict withKey:@"a key of your choice"]; 

y recuperarlo más adelante otra vez como esto:

NSDictionary *dict = [self unarchiveForKey:@"a key of your choice"]; 
-2

si necesita almacenar;

  • datos de un objeto personalizado,
  • o una matriz de objetos personalizados

puede utilizar métodos NSKeyedArchiver. Puede consultar el answer de Leviathan para este método.


Sin embargo, si usted está tratando de almacenar;

  • un diccionario que contiene ya sea NSString o NSNumber (como un diccionario convertido de una respuesta de servicio JSON),
  • o matriz de este tipo de diccionario

que no es necesario utilizar NSKeyedArchiver . Puede usar los valores predeterminados del usuario.


En mi caso, cuando recupero el diccionario de valores predeterminados del usuario que regresaba a cero, así que pensé NSUserDefaults no está dispuesto a salvar mi diccionario.

Sin embargo, se guardó, pero estaba usando el método getter incorrecto para recuperarlo de los valores predeterminados del usuario;

[[NSUserDefaults standardUserDefaults] stringForKey:<my_key>] 

Por favor, asegúrese de haber utilizado cualquiera;

[[NSUserDefaults standardUserDefaults] dictionaryForKey:<my_key>] 

o;

[[NSUserDefaults standardUserDefaults] objectForKey:<my_key>] 

antes de comprobar cualquier otra posible razón.

+0

¿Por qué debería usar objectForKey por defecto? Creo que el casting explícito no es una buena práctica y debería evitarse. También mi comentario corrige exactamente el mismo problema, ¿no? Al votar negativamente mi comentario (si lo hizo) está inhabilitando a las personas para llegar a un ejemplo de trabajo. @Jaro – Berk

+0

1: no es la respuesta a la pregunta porque la conversión de un diccionario a una cadena es posible y soluciona el problema en este caso PERO eso no resuelve el problema de tener objetos dentro de un diccionario que hace que un Dictianary no pueda se guardará en NSUserDefaults .. 2: implícita es una mejor opción de diseño, creo. Mi razón es evitar estos (muchas veces inesperados) errores que tuvo. No quiero dejar los doodies de comprobación de tipos para los NSUserDefaults. Es mejor escribir los métodos Archiver si quieres ser tan explícito. – Jaro

+0

1: Si los datos principales son diccionarios, convertirlos en una cadena es inútil, no es necesario, mala programación. No digo conversión necesaria. [NSUserDefaults dictionaryForKey: MY_KEY] no convierte datos en absoluto. Pero si guarda los datos principales con un tipo e intenta recuperarlos con otro tipo, devuelve nada, ya que nunca se guardó. El problema principal no es que los datos no se guarden, sino que causan exactamente el mismo síntoma. 2: evitar errores tipográficos es el problema principal de IDE y el programador en sí. El diseño de la programación debe incluir otras inquietudes, como el costo de la prueba unitaria, la cohesión, las dependencias claras, etc. – Berk

Cuestiones relacionadas