Perché NSUserDefaults non è riuscito a salvare NSMutableDictionary in iOS?


145

Vorrei salvare un NSMutableDictionaryoggetto NSUserDefaults. Il tipo di chiave NSMutableDictionaryè NSString, il tipo di valore è NSArray, che contiene un elenco di oggetti che implementa NSCoding. Per documento NSStringed NSArrayentrambi sono conformi NSCoding.

Ricevo questo errore:

[NSUserDefaults setObject: forKey:]: tentativo di inserire un valore non di proprietà .... della classe NSCFDictionary.

Risposte:


230

Ho scoperto un'alternativa, prima di salvare, ho codificato l'oggetto root ( NSArrayoggetto) utilizzando NSKeyedArchiver, che termina con NSData. Quindi utilizzare UserDefaults salvare il NSData.

Quando ho bisogno dei dati, leggo NSDatae uso NSKeyedUnarchiverper riconvertire NSDatal'oggetto.

È un po 'ingombrante, perché devo convertirlo in / da NSDataogni volta, ma funziona.

Ecco un esempio per richiesta:

Salva:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *arr = ... ; // set value
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arr];
[defaults setObject:data forKey:@"theKey"];
[defaults synchronize];

Caricare:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:@"theKey"];
NSArray *arr = [NSKeyedUnarchiver unarchiveObjectWithData:data];

L'elemento nella matrice implementa

@interface CommentItem : NSObject<NSCoding> {
    NSString *value;
}

Quindi, nell'implementazione di CommentItem, fornisce due metodi:

-(void)encodeWithCoder:(NSCoder *)encoder
{
    [encoder encodeObject:value forKey:@"Value"];
}

-(id)initWithCoder:(NSCoder *)decoder
{
    self.value = [decoder decodeObjectForKey:@"Value"];
    return self;
}

Qualcuno ha una soluzione migliore?

Grazie a tutti.


4
Hai un esempio di codice che puoi condividere, ho provato a farlo in post (537044) ma senza gioia
Anthony Main,

Questo ha funzionato bene per me. Stavo cercando di codificare un NSArray di oggetti NSDictionary in cui alcune delle chiavi non erano stringhe. Semplicemente usando NSKeyedArchiver e Unarchiver su NSArray è stato eliminato l'errore "Tentativo di inserimento di un valore non di proprietà".
John Wright,

Quando devo rilasciare l'oggetto caricato? Non è stato chiamato alcun alloc, quindi suppongo che sia stato cancellato automaticamente?
Marc,

7
potremmo aver bisogno di aggiungere [defaults synchronize] per il salvataggio
thanhbinh84

La soluzione pubblicata qui potrebbe funzionare per un po ', ma quando decidi di rimuovere o modificare la classe, ti ritroverai bloccato in un pasticcio. Una soluzione migliore è fare in modo che il tuo oggetto scriva il suo stato usando NSNumbers, Strings, ecc. In un dizionario, quindi memorizzalo. Prova futura.
Tom Andersen,

105

Se si salva un oggetto nelle impostazioni predefinite dell'utente, tutti gli oggetti, ricorsivamente, fino in fondo, devono essere oggetti dell'elenco proprietà. Conformarsi a NSCoding non significa nulla qui-- NSUserDefaultsnon li codificherà automaticamente NSData, devi farlo tu stesso. Se il tuo "elenco di oggetti che implementa NSCoding" indica oggetti che non sono oggetti dell'elenco di proprietà, dovrai fare qualcosa con loro prima di salvare le impostazioni predefinite dell'utente.

FYI le classi di elenco di proprietà sono NSDictionary, NSArray, NSString, NSDate, NSData, e NSNumber. Puoi scrivere sottoclassi mutabili (come NSMutableDictionary) alle preferenze dell'utente, ma gli oggetti che leggi saranno sempre immutabili.


61
Dovrei anche menzionare che un NSDictionary è solo un oggetto dell'elenco delle proprietà se le chiavi sono NSStrings. In generale è possibile utilizzare altri oggetti come chiavi del dizionario, ma dove sono richiesti elenchi di proprietà le chiavi devono essere stringhe.
Tom Harrington,

Mi sono imbattuto nello stesso problema, quando volevo archiviare NSURL come oggetto in un NSDictionary e memorizzarlo in NSUserDefaults. Ho dovuto cambiarlo in un NSString, che ha funzionato.
NicTesla,

1
Grande! Nel mio caso, ho provato a usare NSNumber come chiave di NSDictionary nelle impostazioni predefinite dell'utente. Devo cambiarlo in una NSString.
Joey,

1
Tale comportamento ritardato è ciò che porta i programmatori inesperti a scrivere l'intero modello di dati come dizionari invece di creare oggetti dati adeguati? Quanto può essere faticoso per Apple scrivere classi di base che utilizzano il fatto che obj-c ha introspezione e si occupa di boxe e unboxing per noi?
ArtOfWarfare l'

14

Tutte le tue chiavi sono nel dizionario NSString? Penso che debbano essere per salvare il dizionario in un elenco di proprietà.


1
le chiavi sono NSString. ma il valore è NSArray, il cui elemento è una classe personalizzata che implementa NSCoding. Sembra che la soluzione non funzioni perché UserDefaults supporta solo 6 tipi di classe, non considerare NSCoding.
BlueDolphin,

8

Risposta più semplice:

NSDictionaryè solo un oggetto plist , se le chiavi sono NSStrings . Quindi, memorizzare la "chiave" come NSStringcon stringWithFormat.


Soluzione:

NSString *key = [NSString stringWithFormat:@"%@",[dictionary valueForKey:@"Key"]];

Benefici :

  1. Aggiungerà String-Value .
  2. Aggiungerà il valore vuoto quando il valore della variabile è NULL.

2
Ho avuto un problema simile: le mie chiavi erano NSNumber, che apparentemente non sono consentite nelle chiavi dei dizionari di plist.
Mark Pauley,

5

Hai considerato di applicare il NSCodingprotocollo? Ciò ti consentirà di codificare e decodificare su iPhone con due semplici metodi implementati con NSCoding. Per prima cosa dovresti aggiungere il NSCodingalla tua classe.

Ecco un esempio:

Questo è nel file .h

@interface GameContent : NSObject <NSCoding>

Quindi dovrai implementare due metodi del protocollo NSCoding.

    - (id) initWithCoder: (NSCoder *)coder
    {
        if (self = [super init])
        {
               [self setFoundHotSpots:[coder decodeObjectForKey:@"foundHotSpots"]];
        }
        return self;
    }

    - (void) encodeWithCoder: (NSCoder *)coder
    {
           [coder encodeObject:foundHotSpots forKey:@"foundHotSpots"];
    }

Consulta la documentazione su NSCoder per ulteriori informazioni. È stato molto utile per i miei progetti in cui ho bisogno di salvare lo stato dell'applicazione su iPhone se l'applicazione è chiusa e ripristinarla allo stato quando è di nuovo accesa.

La chiave è aggiungere il protocollo all'interfaccia e quindi implementare i due metodi di cui fanno parte NSCoding.

Spero che aiuti!


0

Non esiste una soluzione migliore. Un'altra opzione sarebbe quella di salvare l'oggetto codificato su disco, ma sta facendo la stessa cosa. Entrambi finiscono con NSData che viene decodificato quando lo si desidera indietro.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.