2009-08-11 7 views
3

je dois sauver ma propre classe créée pour déposer, je l'ai trouvé sur internet, que la bonne approche consiste à utiliser NSKeyedArchiver et NSKeyedUnarchiver Ma définition de classe ressemble à ceci:problème iPhone sérialisation

@interface Game : NSObject <NSCoding> { 

    NSMutableString *strCompleteWord; 
    NSMutableString *strWordToGuess; 

    NSMutableArray *arGuessedLetters; //This array stores characters 
    NSMutableArray *arGuessedLettersPos; //This array stores CGRects 

    NSInteger iScore; 
    NSInteger iLives; 
    NSInteger iRocksFallen; 

    BOOL bGameCompleted; 
    BOOL bGameOver; 
} 

J'ai les méthodes mises en œuvre initWithCoder: et encodeWithCoder: ainsi:

- (id)initWithCoder:(NSCoder *)coder 
    { 
     if([coder allowsKeyedCoding]) 
     { 
      strCompleteWord = [[coder decodeObjectForKey:@"CompletedWord"] copy]; 
      strWordToGuess = [[coder decodeObjectForKey:@"WordToGuess"] copy]; 
      arGuessedLetters = [[coder decodeObjectForKey:@"GuessedLetters"] retain]; 
     // arGuessedLettersPos = [[coder decodeObjectForKey:@"GuessedLettersPos"] retain]; 
      iScore = [coder decodeIntegerForKey:@"Score"]; 
      iLives = [coder decodeIntegerForKey:@"Lives"]; 
      iRocksFallen = [coder decodeIntegerForKey:@"RocksFallen"]; 
      bGameCompleted = [coder decodeBoolForKey:@"GameCompleted"]; 
      bGameOver = [coder decodeBoolForKey:@"GameOver"]; 
     } 
     else 
     { 
      strCompleteWord = [[coder decodeObject] retain]; 
      strWordToGuess = [[coder decodeObject] retain]; 
      arGuessedLetters = [[coder decodeObject] retain]; 
     // arGuessedLettersPos = [[coder decodeObject] retain]; 
      [coder decodeValueOfObjCType:@encode(NSInteger) at:&iScore]; 
      [coder decodeValueOfObjCType:@encode(NSInteger) at:&iLives]; 
      [coder decodeValueOfObjCType:@encode(NSInteger) at:&iRocksFallen]; 
      [coder decodeValueOfObjCType:@encode(BOOL) at:&bGameCompleted]; 
      [coder decodeValueOfObjCType:@encode(BOOL) at:&bGameOver]; 
     } 

     return self; 
    } 

    - (void)encodeWithCoder:(NSCoder *)coder 
    { 
     if([coder allowsKeyedCoding]) 
     { 
      [coder encodeObject:strCompleteWord forKey:@"CompleteWord"]; 
      [coder encodeObject:strWordToGuess forKey:@"WordToGuess"]; 
      [coder encodeObject:arGuessedLetters forKey:@"GuessedLetters"]; 
      //[coder encodeObject:arGuessedLettersPos forKey:@"GuessedLettersPos"]; 
      [coder encodeInteger:iScore forKey:@"Score"]; 
      [coder encodeInteger:iLives forKey:@"Lives"]; 
      [coder encodeInteger:iRocksFallen forKey:@"RocksFallen"]; 
      [coder encodeBool:bGameCompleted forKey:@"GameCompleted"]; 
      [coder encodeBool:bGameOver forKey:@"GameOver"]; 
     } 
     else 
     { 
      [coder encodeObject:strCompleteWord]; 
      [coder encodeObject:strWordToGuess]; 
      [coder encodeObject:arGuessedLetters]; 
      //[coder encodeObject:arGuessedLettersPos]; 
      [coder encodeValueOfObjCType:@encode(NSInteger) at:&iScore]; 
      [coder encodeValueOfObjCType:@encode(NSInteger) at:&iLives]; 
      [coder encodeValueOfObjCType:@encode(NSInteger) at:&iRocksFallen]; 
      [coder encodeValueOfObjCType:@encode(BOOL) at:&bGameCompleted]; 
      [coder encodeValueOfObjCType:@encode(BOOL) at:&bGameOver]; 
     } 
    } 

et j'utiliser ces méthodes pour archiver les données et décompressez:

[NSKeyedArchiver archiveRootObject:currentGame toFile:strPath]; 
Game *currentGame = [NSKeyedUnarchiver unarchiveObjectWithFile:strPath]; 

J'ai deux problèmes. 1) Comme vous pouvez le voir, les lignes arGuessedLettersPos sont commentées, parce que chaque fois que j'essaye de coder ce tableau, une erreur survient (cet archiveur ne peut pas coder les structures), et ce tableau est utilisé pour stocker les structures CGRect. J'ai vu une solution sur internet. Le fait est que tous les CGRect du tableau sont convertis en NSString (en utilisant NSStringFromCGRect()), puis enregistrés. Est-ce une bonne approche?

2) Ceci est un plus gros problème pour moi. Même si je commente cette ligne, puis exécute le code avec succès, puis enregistrez (archive) les données et ensuite essayez de les charger (désarchiver), aucune donnée n'est chargée. Il n'y a pas d'erreur mais l'objet currentGame n'a pas de données à charger.

Pourriez-vous me donner quelques conseils? C'est la première fois que j'utilise des archivers et des unarchivers.

Merci beaucoup pour chaque réponse.

Répondre

1

Je l'ai peut-être manqué, mais je ne vois pas de bogues évidents dans ce code.

Voici quelques idées qui pourraient aider:

  • Ajouter quelques NSLog déclarations, et de regarder pour voir si vos méthodes encode/décode la sortie de débogage (ouvert avec la commande-shift-R dans Xcode) sont être appelé.
  • Vérifiez que le fichier d'archive est enregistré: En cours d'exécution dans le simulateur, vous pouvez enregistrer dans le chemin local de votre choix, tel que/tmp/mon_fichier_archive. Essayez d'enregistrer dans ce fichier, et voir si (a) le fichier existe avec le bon horodatage, et (b) vous imprimez le fichier, vous pouvez voir des chaînes reconnaissables (comme "RocksFallen") dans le binaire gooblygoo.

Je ne pense pas aussi il est nécessaire de vérifier allowsKeyed(En)coding puisque vous savez que ça va toujours être vrai lorsque vous utilisez explicitement NSKeyed(Un)archiver pour faire votre sale boulot pour vous. Vous pouvez donc jeter l'autre moitié de votre code. A propos de coder ces tableaux de CGRect s: Je ne pense pas que vous pouvez directement ajouter des structures à un NSMutableArray, non? Donc ils doivent être une sorte d'objet, ce qui signifie que vous pouvez ajouter NSCoding support à cet objet. Si ce n'est pas votre propre objet, vous pouvez le sous-classer et ajouter ce protocole, ou essayer de l'ajouter en tant que catégorie.

0

Merci pour la réponse Tyler.J'ai regardé dans le fichier enregistré et il y a des chaînes reconnaissables comme RocksFallen et ainsi de suite, il y a aussi des valeurs reconnaissables qui pourraient être sauvegardées dans des variables de chaîne. Donc, cela semble être bon.

J'ai essayé de mettre quelques NSLogs dans le code et tout ne semble pas être bon. Alors ... Quand je lance un simulateur pour la première fois, il n'y a pas de fichier archive, oui c'est évident, car rien n'est sauvegardé. Ensuite, je change quelque chose et quitte l'application. Lorsque l'application est terminée, les données peuvent être archivées, tout va bien. Les instructions NSLog sont écrites dans la console, le fichier est sur le disque. Mais ce qui est étrange, c'est que lorsque je lance l'application, la méthode de décodage (initwithcoder) n'est pas appelée. Je ne sais pas pourquoi. Ensuite, je quitte le simulateur et relance le simulateur. Quand je l'exécute à nouveau, la méthode de décodage est appelée au lancement. Mais seulement au premier lancement après l'exécution du simulateur, quand je travaille ensuite avec le simulateur comme quitter l'application et l'exécuter à nouveau, la méthode initWithCoder n'est pas appelée et c'est très étrange pour moi ..

Donc il y a des problèmes de désarchiv pense.

+0

On dirait que vous avez de bons indices pour travailler avec. Vous pouvez vérifier dans le code que le fichier désiré existe. Il peut être sauvegardé dans un emplacement temporaire, ou il peut y avoir une faute de frappe dans le nom du fichier, etc. Une autre idée consiste simplement à ajouter un NSLog juste avant votre appel à unarchiveObjectWithFile: pour vérifier que cette ligne exacte est en cours d'exécution. Si vous vous sentez aventureux, vous pouvez ajouter un point d'arrêt et parcourir le code. – Tyler

2

Le problème avec le chargement et la sauvegarde résolu d'une autre manière ...

Au lieu de mettre en œuvre - (id) initWithCoder: (NSCoder ) codeur et - (void) encodeWithCoder: (NSCoder) codeur I utilisé cette solution :

NSMutableData *data = [NSMutableData alloc]; 
    NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; 

    [archiver encodeObject:self.strCompleteWord forKey:@"CompleteWord"]; 
    [archiver encodeObject:self.strWordToGuess forKey:@"WordToGuess"]; 
    [archiver encodeObject:self.arGuessedLetters forKey:@"GuessedLetters"]; 
    //[coder encodeObject:self.arGuessedLettersPos forKey:@"GuessedLettersPos"]; 
    [archiver encodeInteger:self.iScore forKey:@"Score"]; 
    [archiver encodeInteger:self.iLives forKey:@"Lives"]; 
    [archiver encodeInteger:self.iRocksFallen forKey:@"RocksFallen"]; 
    [archiver encodeBool:self.bGameCompleted forKey:@"GameCompleted"]; 
    [archiver encodeBool:self.bGameOver forKey:@"GameOver"]; 

    [archiver finishEncoding]; 

    [data writeToFile:strPath atomically:YES]; 

    [data release]; 

et

NSMutableData *data = [[NSMutableData alloc] initWithContentsOfFile:strPath]; 
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; 

    self.strCompleteWord = [[unarchiver decodeObjectForKey:@"CompletedWord"] copy]; 
    self.strWordToGuess = [[unarchiver decodeObjectForKey:@"WordToGuess"] copy]; 
    self.arGuessedLetters = [[unarchiver decodeObjectForKey:@"GuessedLetters"] retain]; 
    //self.arGuessedLettersPos = [[unarchiver decodeObjectForKey:@"GuessedLettersPos"] retain];  
    self.iScore = [unarchiver decodeIntegerForKey:@"Score"]; 
    self.iLives = [unarchiver decodeIntegerForKey:@"Lives"]; 
self.iRocksFallen = [unarchiver decodeIntegerForKey:@"RocksFallen"]; 
    self.bGameCompleted = [unarchiver decodeBoolForKey:@"GameCompleted"]; 
    self.bGameOver = [unarchiver decodeBoolForKey:@"GameOver"]; 

    [unarchiver finishDecoding]; 
    [data release]; 

et cela fonctionne tout à fait bien :)

0

La meilleure façon d'archiver des structures (qui ne contiennent pas de pointeurs) serait d'envelopper les valeurs dans un bloc NSData.

ici, je suis le dumping un tableau de struct au archiveur:

for (int i = 0; i < items; i++) { 

    NSString *keyValue = [NSString stringWithFormat:@"YourStruct%i", i ]; 
    NSData *dataset = [NSData dataWithBytes:(void*)&activeprofile[i] length:(sizeof(profileset))]; 

    [encoder encodeObject:dataset forKey:keyValue]; // makes a nice, solid data block 

} 

et la lecture des données de retour dans:

for (int i = 0; i < items; i++) { 

    NSString *keyValue = [NSString stringWithFormat:@"YourStruct%i", i ]; 
    NSData *dataset = [decoder decodeObjectForKey:keyValue]; 

    [dataset getBytes:&activeprofile[i] length:sizeof(profileset)]; 

} 

Il vous en avez: simple comme bonjour et extrêmement flexible Types de données!

Une méthode similaire pour l'archivage sans clé peut être trouvée ici: http://borkware.com/quickies/one?topic=NSCoder Bien que je suggère de rester avec l'archivage à clé, comme NSArchive n'est pas pris en charge sur l'iPhone et a été classé comme code hérité.