Pb avec NSUnarchiver
Bonjour à tous.
Il m'arrive un problème qui est plutôt bizarre. En effet, j'archive une hiérarchie d'objets de classe CXTreeNode identique à l'aide d NSArchiver. J'effectue bien évidemment l'inverse à l'aide de NSUnarchiver.
J'ai doté CXTreeNode des deux méthodes issues du protocole NSCoding.
Les traces m'indiquent que l'archivage se déroulent sans erreur et le désarchivage également.
Le problème intervient dans l'invocation de la méthode suivante
_tree = [NSUnarchiver unarchiveObjectWithFile:inPath];
Le code plante et le débogueur indique
Program received signal: "EXC_BAD_ACCESS".
La pile d'exécution localise le bug au niveau de [NSUnarchiver dealloc] qui invoque objc_msgSend.
Ce qui est bizarre, c'est que les méthodes de classe créent des instances en destruction automatique. Cette opération intervient après la fin de l'éxécution de l'événement.
Je ne comprend pas pourquoi la destruction intervient ici !
Quelqu'un a une idée. Merci de son aide.
Il m'arrive un problème qui est plutôt bizarre. En effet, j'archive une hiérarchie d'objets de classe CXTreeNode identique à l'aide d NSArchiver. J'effectue bien évidemment l'inverse à l'aide de NSUnarchiver.
J'ai doté CXTreeNode des deux méthodes issues du protocole NSCoding.
Les traces m'indiquent que l'archivage se déroulent sans erreur et le désarchivage également.
Le problème intervient dans l'invocation de la méthode suivante
_tree = [NSUnarchiver unarchiveObjectWithFile:inPath];
Le code plante et le débogueur indique
Program received signal: "EXC_BAD_ACCESS".
La pile d'exécution localise le bug au niveau de [NSUnarchiver dealloc] qui invoque objc_msgSend.
Ce qui est bizarre, c'est que les méthodes de classe créent des instances en destruction automatique. Cette opération intervient après la fin de l'éxécution de l'événement.
Je ne comprend pas pourquoi la destruction intervient ici !
Quelqu'un a une idée. Merci de son aide.
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
As-tu vérifié si inPath est correcte ?
Utilises-tu des NSKeyedArchiver ou des NSArchiver, as-tu respecté les modèles d'archivage
"unArchiveObjectWithFile returns nil if the file at path cannot be unarchived." ce qui fait que _tree peut valoir nil ...
Il n'y a pas de questions stupides lorsque l'on essaye de comprendre un problème si curieux !
inPath est correct. Le chargement de l'archive s'effectue normalement car les traces dans initWithCoder de CXTreeNode récupèrent les données membres dans le même ordre que celui de l'archivage, logique car il s'agit d'un flux. Chaque noeud de l'arbre est matérialisé par un objet de cette classe.
L'archivage s'effectue uniquement avec NSArchiver. J'ai implémenté la méthode encodeWithCoder. Puisque CXTreeNode hérite de NSObject et que cette dernière ne supporte pas le protocole NSCoding, il n'y a donc pas d'appel à la super classe.
Quant à l'instruction, elle plante avant l'assignation à la variable _tree.
Je ne comprend pas pouquoi le destructeur est invoqué comme le montre la vue du debogueur.
Effectivement, chaque noeud détient la référence de son parent ainsi que la liste de ses enfants. Il mémorise également une donnée sous la forme d'un NSMutableDictionary ainsi qu'un indicateur précisant s'il peut avoir une descendance. L'encodage est réalisé par la méthode suivante:
Le décodage est réalisé par la méthode suivante:
Il s'agit d'un arbre, c'est à dire un graphe acyclique. Ainsi tout noeud ne peut pas avoir un ascendant comme enfant.
Ce qui m'obsède, c'est le destructeur. C'est l'inverse de la convention d'Apple !
Le pire est que tout fonctionne. L'archivage se déroule sans problème. J'obtiens un fichier. Le désarchivage se déroule bien jusqu'à l'appel de ce dealloc incongru !
Petite remarque: L'archivage s'effectue en deux passes. j'ai stoppé le processus avant le lancement de la deuxième phase et enregistré le résultat. Il semble que la première passe mémorise la structure (le graphe) tandis que la seconde semble mémoriser les noeuds du graphe ainsi obtenu. Bien évidemment, il faudrait inspecter plus pour certifier.
Merci de ton aide.[/size]
2) L'encodage doit concerner aussi super : [super encodeWithCoder:coder];
doc : When to Retain a Decoded Object
You can decode an object value in two ways. The first is explicitly, using the decodeObject or decodeObjectForKey: method. When decoding an object explicitly you must follow the object ownership convention and retain the object returned if you intend to keep it. Otherwise, the object is owned by the coder and the coder is responsible for releasing the object.
The second means of decoding an object is implicitly, using the decodeValueOfObjCType:at: method or one of its variants, decodeArrayOfObjCType:count:at: and decodeValuesOfObjCTypes:. These methods decode values directly into memory that you provide. In the case of objects, the value is the object pointer. As this memory is already owned by you, you are responsible for releasing the objects decoded into it. This behavior can prove useful for optimizing large decoding operations, as it obviates the need for sending a retain message to each decoded object.
En conclusion, il fallait faire:
• Retenir chaque donnée membre.
• Retenir l'objet CXTreeNode (self) au niveau de initWithCoder.
NSUnarchiver alloue temporairement ce qu'il désarchive.
Tout celà n'est pas très logique car si l'on veut désarchiver un graphe d'objets, c'est pour travailler dessus sans limite de durée de vie des objets réanimés.
Les méthodes de classe évite de se préoccuper de la destruction de l'objet utilisé et pas des objets impliqués. Dans notre cas, il s'agissait de NSUnarchiver et non pas du graphe.
Et encore merci à Philippe49 et Aligator.
Je n'ai jamais vu faire [self retain] dans un init.
As-tu rajouté [super encodeWithCoder:coder], c'est nécessaire pour que l'archive contienne les données de la classe parente de l'objet.
Oui et non, c'est la stratégie générale d'initialisation qui est comme ça. Quand on initialise un champ dans une méthode -(id) init, il faut bien faire le retain, et pourtant on veut bien travailler avec après.
En ce qui concerne le [self retain], bien évidemment cet appel est absude! Une over dose de chocolat sans aucun doute pour avancer le projet !
Le seul noe“ud a retenir est le noe“ud racine ce qui s'écrit simplement par:
Le second point concerne les appels des méthodes de la super classe. Effectivement , l'archivage d'un objet consiste à enregistrer uniquement les données membre de l'objet en question. Si l'objet hérite d'une classe supportant le protocole NSCoding, il faut effectivement invoquer la méthode correspondante.
Dans notre cas, CXTreeNode hérite de NSObject. Cette dernière ne supporte pas le protocole donc, il n'y a pas d'appel supplémentaire.
Ce principe s'applique également à NSUnarchiver.
J'ai retesté le code dans son ensemble et tout fonctionne. Les objets dans la hiérarchie ont tous leur compteur de retenue à 1 à la fois pour l'archivage et le désarchivage.
Encore merci, Philippe pour ton aide.
Ok merci de rappeler ce point.(Je pensais plus ou moins confusément que CXTreeNode héritait de NSTreeNode)