2012-02-24 16 views
13

Sto usando KVC per serializzare un NSObject e tentare di salvarlo su NSUserDefaults, che mi sta dando un Attempt to insert non-property value quando provo a memorizzare il mio NSDictionary.Perché NSUserDefaults non è disposto a salvare il mio NSDictionary?

di seguito le proprietà dell'oggetto in questione, 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 

Quando è il momento di salvare MyClass si verifica qui:

-(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]; 
} 

che produce questo output:

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.` 

Come si può vedere, tutti gli oggetti nello dict sono pro i valori di lista perty e tutte le sue chiavi sono NSString*. Che trivia mi manca per poterlo eseguire? O dovrei rinunciare e usare writeToFile o simili?

+1

Stai solo stampando le chiavi. E i valori? –

+0

Questo è tutto, uno dei miei valori si intromette in un 'NSNull' (JSON' null' prodotto da 'JSONKit') e questa è quasi sicuramente la causa. Non ho ancora lavorato a una soluzione. –

risposta

20

Puntelli a Kevin che ha suggerito di stampare i valori, ovviamente uno dei quali è di tipo NSNull che non è un valore di elenco di proprietà. Grazie!

La soluzione kludgy al mio problema - scorrere le chiavi del dizionario che ho appena prodotto con dictionaryWithValuesForKeys e rimuovere quelli di tipo NSNull. sospiro

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 ti odio 'JSONKit' e il tuo! @ #% @ #! 'NSNull's –

+1

Ho determinato che' [dict removeObjectForKey: key]; 'è in realtà una soluzione insufficiente per il mio problema, perché ora i valori mancanti non verranno mai sovrascritti in' NSUserDefaults'. Invece, sto salvando una stringa vuota quando viene rilevato un 'NSNull', come riflesso nella risposta. –

+0

cool! : D Questa soluzione funziona per me. Grazie –

18

Io di solito archivio e disarchivi dizionari quando li salvano ai valori predefiniti dell'utente. In questo modo non è necessario verificare manualmente i valori NSNull.

Basta aggiungere i seguenti due metodi al codice. Potenzialmente in una categoria.

- (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; 
} 

Poi si può archiviare qualsiasi dizionario come questo (supponendo che il metodo sono disponibili nella classe):

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

e recuperare in un secondo momento ancora una volta come questo:

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

Se è necessario archiviare;

  • i dati di un oggetto personalizzato,
  • o un array di oggetti personalizzati

è possibile utilizzare metodi NSKeyedArchiver. Puoi controllare leviathan's answer per questo metodo.


Tuttavia, se si sta cercando di memorizzare;

  • un dizionario che contiene sia NSString o NSNumber (come un dizionario convertito da una risposta del servizio JSON),
  • o array di questo tipo di dizionario

non c'è bisogno di usare NSKeyedArchiver . È possibile utilizzare le impostazioni predefinite dell'utente.


Nel mio caso, quando ho recuperare il dizionario da impostazioni predefinite dell'utente che era di ritorno pari a zero, così ho pensato NSUserDefaults non sia disposto a salvare il mio dizionario.

Tuttavia, è stato salvato, ma stavo usando il metodo getter sbagliato per recuperarlo dai valori predefiniti dell'utente;

[[NSUserDefaults standardUserDefaults] stringForKey:<my_key>] 

Assicurarsi di averlo utilizzato;

[[NSUserDefaults standardUserDefaults] dictionaryForKey:<my_key>] 

oppure;

[[NSUserDefaults standardUserDefaults] objectForKey:<my_key>] 

prima di verificare qualsiasi altro possibile motivo.

+0

Perché dovrei usare objectForKey di default? Penso che il casting esplicito non sia una buona pratica e dovrebbe essere evitato. Anche il mio commento risolve esattamente lo stesso problema, vero? Sottolineando il mio commento (se lo hai fatto) stai disabilitando le persone per raggiungere un esempio funzionante. @Jaro – Berk

+0

1: non è la risposta alla domanda perché la conversione di un dizionario in una stringa è possibile e risolve il problema in questo caso MA non risolve il problema di avere oggetti all'interno di un dizionario il cui risultato è che un Dictianary non è in grado di essere salvato in NSUserDefaults .. 2: implicito è una scelta di design migliore, penso. La mia ragione è di evitare questo (molte volte inaspettato) errori che hai avuto. Non voglio lasciare il tipo che controlla i doodies per NSUserDefaults. È meglio scrivere i metodi Archiver se non vuoi essere così esplicito. – Jaro

+0

1: Se i dati principali sono dizionario, convertirli in una stringa è inutile, non necessario, programmazione errata. Non dico che sia necessaria la conversione. [NSUserDefaults dictionaryForKey: MY_KEY] non converte i dati. Ma se si salvano i dati principali con un tipo e si tenta di recuperarlo con un altro tipo, esso restituisce nil, in quanto non è mai stato salvato. Il problema principale non è che i dati non vengono salvati, ma causa lo stesso identico sintomo. 2: evitare errori di battitura è il problema principale di IDE e del programmatore stesso. Il progetto di programmazione dovrebbe includere altre preoccupazioni, come il costo unitario del test, la coesione, le chiare dipendenze, ecc. – Berk