web-dev-qa-db-fra.com

Pourquoi NSUserDefaults n'a pas pu sauvegarder NSMutableDictionary dans iOS?

J'aimerais enregistrer un objet NSMutableDictionary dans NSUserDefaults. Le type de clé dans NSMutableDictionary est NSString, le type de valeur est NSArray, qui contient la liste des objets qui implémentent NSCoding. Par document, NSString et NSArray sont tous deux conformes à NSCoding.

Je reçois cette erreur:

[NSUserDefaults setObject: forKey:]: Tentative d'insérer une valeur autre que la propriété .... de la classe NSCFDictionary.

145
BlueDolphin

J'ai découvert une alternative, avant de sauvegarder, je codais l'objet racine (NSArray object) en utilisant NSKeyedArchiver, qui se terminait par NSData. Ensuite, utilisez UserDefaults et sauvegardez le NSData.

Lorsque j'ai besoin des données, je lis le NSData et utilise NSKeyedUnarchiver pour convertir NSData en objet.

C'est un peu lourd, car je dois convertir à/de NSData à chaque fois, mais ça fonctionne.

Voici un exemple par demande:

Sauver:

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

Charge:

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

L'élément dans le tableau implémente

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

Ensuite, dans l'implémentation de CommentItem, fournit deux méthodes:

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

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

Quelqu'un a une meilleure solution?

Merci tout le monde.

230
BlueDolphin

Si vous enregistrez un objet dans les valeurs par défaut de l'utilisateur, tous les objets, de manière récursive, jusqu'au bout, doivent être des objets de liste de propriétés. Se conformer à NSCoding ne veut rien dire ici _ NSUserDefaults ne les encodera pas automatiquement dans NSData, vous devez le faire vous-même. Si votre "liste d'objet qui implémente NSCoding" désigne des objets qui ne sont pas des objets de liste de propriétés, vous devrez alors en faire quelque chose avant de sauvegarder les paramètres par défaut de l'utilisateur.

Pour votre information, les classes de la liste de propriétés sont NSDictionary, NSArray, NSString, NSDate, NSData et NSNumber. Vous pouvez écrire des sous-classes mutables (comme NSMutableDictionary) dans les préférences de l'utilisateur, mais les objets que vous lisez seront toujours immuables.

105
Tom Harrington

Toutes vos clés sont-elles dans le dictionnaire NSStrings? Je pense qu'ils doivent être afin de sauvegarder le dictionnaire dans une liste de propriétés.

14
Sophie Alpert

Réponse la plus simple:

NSDictionary n'est qu'un objet plist, si les clés sont NSStrings. Donc, stockez le "Key" comme NSString avec stringWithFormat.


Solution:

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

Avantages:

  1. Il va ajouter String-Value .
  2. Il ajoutera une valeur vide lorsque votre valeur de variable est NULL.
7
Bhavin

Avez-vous envisagé de mettre en œuvre le protocole NSCoding? Cela vous permettra d’encoder et de décoder sur l’iPhone avec deux méthodes simples qui sont implémentées avec le NSCoding. Vous devez d’abord ajouter le NSCoding à votre classe.

Voici un exemple:

C'est dans le fichier .h

@interface GameContent : NSObject <NSCoding>

Ensuite, vous devrez implémenter deux méthodes du protocole 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"];
    }

Consultez la documentation sur NSCoder pour plus d'informations. Cela s’est avéré très utile pour mes projets où je dois sauvegarder l’état de l’application sur l’iPhone si l’application est fermée et la restaurer dans son état initial.

La clé consiste à ajouter le protocole à l'interface, puis à implémenter les deux méthodes qui font partie de NSCoding.

J'espère que ça aide!

5
Niels Hansen

Il n'y a pas de meilleure solution. Une autre option serait simplement de sauvegarder l’objet codé sur le disque, mais c’est la même chose. Ils se retrouvent tous les deux avec NSData qui est décodé lorsque vous le souhaitez.

0
Dan Keen