Sauvegarde Plist

AdamAdam Membre
02:17 modifié dans Vos applications #1
Bonjour,

J'entame à  présent ma seconde application, et j'ai un petit soucis.
J'ai une classe Personne avec différents attributs (ex: Personne.nom, Personne.prénom...).

Chaque personne est ajouté dans un tableau :
<br />&nbsp; [tableauUtilisateur addObject:Personne1];&nbsp; <br />



Par la suite, je veux pouvoir sotcker ces "Personnes" dans un fichier Plist. Je crée donc un nouveau dictionnaire, dans lequel j'ajoute mon tableauUtilisateur.
Le problème c'est que le dictionnaire n'est pas sauvegardé dans la Plist.
Pourtant le code me semble correcte :
<br />&nbsp; //Sauvegarde des données<br />&nbsp; &nbsp; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);<br />&nbsp; &nbsp; NSString *documentsDirectory = [paths objectAtIndex:0];<br />&nbsp; &nbsp; NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:@&quot;preferenceUtilisateur.plist&quot;];<br />&nbsp; &nbsp; NSString *errorDesc;<br />&nbsp; &nbsp; NSMutableDictionary *prefDict;<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; <br />&nbsp; &nbsp; //On remplace par les valeur que l&#039;on a modifiées<br />&nbsp; &nbsp; prefDict=[[NSMutableDictionary alloc] initWithObjects:tableauUtilisateur forKeys:[NSArray arrayWithObject:@&quot;utilisateur&quot;]];<br />&nbsp; &nbsp; [prefDict writeToFile:plistPath atomically:YES];<br />&nbsp; &nbsp; NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:prefDict<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  format:NSPropertyListXMLFormat_v1_0<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  errorDescription:&amp;errorDesc];<br />&nbsp; &nbsp; <br />&nbsp; <br />&nbsp; &nbsp; if (plistData) {<br />&nbsp; &nbsp; &nbsp; &nbsp; [plistData writeToFile:plistPath atomically:YES];<br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; else {<br />&nbsp; &nbsp; &nbsp; &nbsp; [errorDesc release];<br />&nbsp; &nbsp; }<br /><br /><br />&nbsp; &nbsp; [prefDict release];<br />


Je pense que j'ai fait une erreur par rapport à  mon tableauUtilisateur, non ? On peut stocker des entités dans une Plist ?
Quand je dis entité, je parle d'une "Personne" avec ses attributs qui est sous la forme :"<Personne: 0x4e2b750>"

Merci de votre aide.

Réponses

  • LexxisLexxis Membre
    02:17 modifié #2
    Salut,
    si mes souvenirs sont bon, il faut que ta classe Personne implémente le protocole NSCoding. De plus il me semble que les seul éléments que tu puisse sauver dans une plist sont: les string (NSString), les tableaux (NSArray) et les dictionnaires (NSDictionnary); j'ai un doutes sur les data (NSData).
    C'est donc assez contraignant. Tu pourrais avantageusement remplacer cette solution par l'utilisation de la classe NSKeyedArchiver ou même de CoreData (qui lui fera presque tout tout seul). Sauf évidemment si tu veux que ton fichier soit "humainement" lisible...
  • AdamAdam Membre
    02:17 modifié #3
    Merci d'avoir répondu aussi vite.

    Comment ça je dois l'implémenter avec NScoding ? J'ai lu la doc, mais ya quelques points qui m'échappe.
    J'essaye de l'intancier, mais je comprend pas trop comment ça fonctionne :
    <br /><br />- (id)initWithCoder:(NSCoder *)decoder<br />{<br />&nbsp; &nbsp; if ((self = [super init]))<br />&nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; NSData *data = [decoder decodeObjectForKey:@&quot;utilisateur&quot;];<br />&nbsp; &nbsp; &nbsp; return data;<br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; return self;<br />}<br /><br />- (void)encodeWithCoder:(NSCoder *)encoder<br />{<br />&nbsp; &nbsp; NSString *errorDesc;<br /><br />&nbsp; &nbsp; NSData *data = [NSPropertyListSerialization dataFromPropertyList:tableauUtilisateur<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; format:NSPropertyListXMLFormat_v1_0<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; errorDescription:&amp;errorDesc];<br />&nbsp; &nbsp; [encoder encodeObject:data forKey:@&quot;utlisateur&quot;];<br />}<br />
    


    Je comprends pas comment l'implementer.

    Et une fois que j'ai ces 2 méthodes, j'utilise data pour les stocker, c'est bien ça ?
  • CéroceCéroce Membre, Modérateur
    02:17 modifié #4
    C'est ta classe Personne qui doit se conformer à  NSCoding.
  • AliGatorAliGator Membre, Modérateur
    mai 2011 modifié #5
    L'implémentation de NSCoding n'est nécessaire que pour utiliser les NSCoders/NSArchivers, donc pour créer des NSArchives. Donc pour prévoir des méthodes pour sérialiser/désérialiser tes classes, dans un format de fichier opaque (propre à  Apple, que tu ne vas pouvoir relire que par le code dans ton soft et pas trop par un logiciel externe). Pas pour faire des PLIST.

    Si tu mets des breakpoints dans ton code, que tu regarde que vaut le paramètre error en retour de la méthode d'écriture du plist, etc, bref que tu débugues un peu pas à  pas, ça donne quoi ?

    Sinon il se trouve que ton code écrit 2 fois ton dictionnaires, dans le plist, en utilisant 2 façons de faire différentes... sauf que toi tu utilises les deux : à  la fois la méthode "writeToFile:atomically:" de NSDictionary qui te permet en effet de sérialiser un NSDictionary dans un fichier PLIST, et la méthode de NSPropertyListSerialization qui permet de faire une sérialisation d'un objet dans un PLIST également... mais de façon plus fine en contrôlant les paramètres du style format du PLIST, etc.
    Ces deux méthodes sont à  peu près équivalentes (l'une permettant de préciser plus de paramètres que l'autre mais bon).

    Par contre, quelle que soit la méthode utilisée, il n'est possible de sérialiser un NSDictionary (tout comme pour un NSArray) |b]que si ce dernier ne contient que des PLIST-Objects[/b] (NSData, NSNumber, NSString, NSDate, NSDictionary, NSArray), que donc Cocoa saura tout seul transformer en leur représentation XML dans le PLIST.
    Or dans ton cas, tu as dans ton NSDictionary ton tableau [tt]tableauUtilisateur[/tt] qui contient... des objets de la classe "Personne" ! Et ça du coup c'est pas possible pour Cocoa de savoir comment les représenter en XML, dans un PLIST.

    Donc dans ce cas de figure, seulement 2 solutions possibles :

    1) Prévoir une représentation intermédiaire de tes objets personne de sorte que cette représentation ne contienne que des PLIST-Objects (par exemple construire, pour chaque objet Personne, un NSDictionary contenant les clés nom, prénom, etc... bref les attributs de ton objet que tu veux écrire dans le plist). Ca t'oblige à  boucler sur chaque Personne de ton tableau le temps de créer un tableau intermédiaire non pas d'objet "Personne" mais de "NSDictionary représentant ces même Personnes", que lui tu pourras stocker dans un PLIST. Et à  la lecture du PLIST, faire l'opération inverse, reconstruire un objet Personne à  partir du NSDictionary récupéré du PLIST. Un peu fastidieux.

    2) Ou bien à  ce stade en effet, autant passer par des NSCoders, donc en faisant en sorte que ta classe Personne implémente le protocole NSCoding (tu as des exemples dans la doc dans le Programming Guide associé). Et dans ce cas quand tu vas sérialiser ton tableau de Personnes, tu vas finir par créer une "archive", qui est un fichier binaire (dans un format interne à  Cocoa dont j'ai jamais cherché à  comprendre l'organisation, pas la peine) qui contiendra tous tes objets "Personne" sérialisés. Et pour la relecture, tu utilises aussi le mécanisme inverse et tu vas directement récupérer ton NSArray d'objets "Personne".
    Avantage tu peux stocker n'importe quel type d'objet du moment que tu implémentes NSCoding. Inconvénient le fichier de sauvegarde ne va plus être dans un format tres compréhensible et ne sera pas éditable avec un outil externe comme Property List Editor, du moins pas facilement vu la tronche du format. Mais après tout est-ce un problème, du moment que ton logiciel, lui, arrive à  écrire et lire cette archive...
  • AdamAdam Membre
    mai 2011 modifié #6
    Merci pour vos réponses, ça deviens un peu plus clair pour moi.

    J'aimerais utiliser la solution utilisant la classe NSCoding.
    La 1ere solution qui consiste de créer un dictionnaire à  chaque utilisateur, c'est ce que j'ai fait au début. Mais comme c'est vraiment pas pratique, j'ai voulu m'orienter vers quelque chose de plus efficace.


    Ah ouai, je faisais fausse piste, j'avais mis le protocle NSCoding dans ma Viewcontroller actuelle et non dans ma classe Personne.

    Donc maintenant j'ai modifié ma classe Personne afin qu'elle corresponde à  NScoding :
    Personne.h :
    <br /><br />@interface Personne : NSObject &lt;NSCoding&gt; {<br /> ...<br />}<br />- (id)initWithCoder:(NSCoder *)decoder; // Restauration<br />- (void)encodeWithCoder:(NSCoder *)encoder; // Archivage<br /><br />
    



    Personne.m :
    <br /><br />- (id)initWithCoder:(NSCoder *)decoder<br />{<br />&nbsp; &nbsp; if ((self = [super init]))<br />&nbsp; &nbsp; {<br />&nbsp; &nbsp;  nomUilisateur = [decoder decodeObjectForKey:@&quot;nom&quot;];&nbsp; &nbsp; &nbsp; <br />&nbsp; &nbsp;  prenomUtilisateur = [decoder decodeObjectForKey:@&quot;prenom&quot;];&nbsp; &nbsp; &nbsp; <br />&nbsp; &nbsp;  identifiant = [decoder decodeObjectForKey:@&quot;identifiant&quot;];&nbsp; &nbsp; &nbsp; <br />&nbsp; &nbsp;  motDePasse = [decoder decodeObjectForKey:@&quot;motDePasse&quot;];&nbsp; &nbsp; &nbsp; <br /><br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; return self;<br />}<br /><br /><br />- (void)encodeWithCoder:(NSCoder *)encoder<br />{&nbsp; <br />&nbsp; &nbsp; [encoder encodeObject:nomUilisateur forKey:@&quot;nom&quot;];<br />&nbsp; &nbsp; [encoder encodeObject:prenomUtilisateur forKey:@&quot;prenom&quot;];<br />&nbsp; &nbsp; [encoder encodeObject:identifiant forKey:@&quot;identifiant&quot;];<br />&nbsp; &nbsp; [encoder encodeObject:motDePasse forKey:@&quot;motDePasse&quot;];<br />}<br /><br />
    


    Concernant ma classe Personne, normalement c'est OK, non ?


    Ensuite je vais dans ma ViewController ou je dois sauvegarder mon tableau d'utilisateur, et je fais donc une Archive que j'encode avec mon tableau :
    <br /><br />&nbsp; NSMutableData *data = [[NSMutableData alloc] init];<br />&nbsp; &nbsp; NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; [archiver encodeObject:tableauUtilisateur forKey:@&quot;utilisateur&quot;];<br />&nbsp; &nbsp; [archiver finishEncoding];<br />&nbsp; &nbsp; [data writeToFile:plistPath atomically:YES];<br />&nbsp; &nbsp; [archiver release];<br />&nbsp; &nbsp; [data release]; <br /><br />
    




    Mais le resultat n'a pas l'air de fonctionner, sa me donne :

    "$archiver" = NSKeyedArchiver;
        "$objects" =    (
            "$null",
                    {
                "$class" = "<CFKeyedArchiverUID 0x4b51f30 [0xe54400]>{value = 7}";
                "NS.objects" =            (
                    "<CFKeyedArchiverUID 0x4b51c80 [0xe54400]>{value = 2}"
                );
            },
                    {
                "$class" = "<CFKeyedArchiverUID 0x4b52110 [0xe54400]>{value = 6}";
                identifiant = "<CFKeyedArchiverUID 0x4b51ff0 [0xe54400]>{value = 4}";
                motDePasse = "<CFKeyedArchiverUID 0x4b52100 [0xe54400]>{value = 5}";
                nom = "<CFKeyedArchiverUID 0x4b520f0 [0xe54400]>{value = 0}";
                prenom = "<CFKeyedArchiverUID 0x4b520e0 [0xe54400]>{value = 3}";
            },
            gui,
            vvvv,
            fvs,
                    {
                "$classes" =            (
                    Utilisateur,
                    NSObject
                );
                "$classname" = Utilisateur;
            },
                    {
                "$classes" =            (
                    NSMutableArray,
                    NSArray,
                    NSObject
                );
                "$classname" = NSMutableArray;
            }
        );
        "$top" =    {
            utilisateur = "<CFKeyedArchiverUID 0x4b51dd0 [0xe54400]>{value = 1}";
        };
        "$version" = 100000;
    }
  • CéroceCéroce Membre, Modérateur
    mai 2011 modifié #7
    dans 1306849399:

    <br />prenomUtilisateur = [decoder decodeObjectForKey:@&quot;prenom&quot;];&nbsp; &nbsp; &nbsp; <br />
    



    Comme son nom l'indique, decodeObjectForKey renvoie un objet autoreleasé. Il ne faut pas oublier de le retenir:
    <br />prenomUtilisateur = [[decoder decodeObjectForKey:@&quot;prenom&quot;] retain]; <br />
    


    Au premier coup d'oe“il, l'enregistrement me parait bon. Le format des PList utilisé par la sérialisation n'est pas fait pour être lisible par un être humain. C'est lié au fait qu'un graph d'objet n'est pas forcément organisé de façon hiérarchique (contrairement au format XML).

    Si tu veux un PList lisible, il vaut mieux utiliser la méthode évoquée par Ali, avec laquelle tu auras plus de contrôle.
  • AdamAdam Membre
    mai 2011 modifié #8
    Ah oui c'est vrai j'avais zappé...

    Mais pour le charger alors, vu qu'à  la base j'ai une archive. J'ai un problème pour récupérer mon tableau de ma nouvelle archive :
    <br />//Recharge les données du Plist<br />&nbsp; &nbsp; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);<br />&nbsp; &nbsp; NSString *documentsDirectory = [paths objectAtIndex:0];<br />&nbsp; &nbsp; NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:@&quot;preferenceUtilisateur.plist&quot;];<br />&nbsp; &nbsp; NSMutableData *data = [[NSMutableData alloc] initWithContentsOfFile:plistPath];<br />&nbsp; &nbsp; NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; tableauUtilisateur=[unarchiver decodeObjectForKey:@&quot;utilisateur&quot;];<br />&nbsp; &nbsp; //tableau qui contient toute les valeurs<br />&nbsp; &nbsp; NSLog(@&quot;Apres la sauvegarde : %@ &quot;,tableauUtilisateur);<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; [unarchiver release];<br />&nbsp; &nbsp; [data release]; <br />
    




    Si je fait un NSLog de tableauUtilisateur, avant la sauvegarde et apres la recharge, j'ai 2 valeurs différentes (ce qui fait bugger ma tableView) :
    2011-05-31 16:42:16.699 AdeUhp[7442:207] Au début on a (avant sauvegarde): (
        "<Utilisateur: 0x4b3bb20>"
    )
    2011-05-31 16:42:18.818 AdeUhp[7442:207] Apres la sauvegarde : (
        "<Utilisateur: 0x4b4e420>"
    )
  • AliGatorAliGator Membre, Modérateur
    02:17 modifié #9
    En effet ce que tu indiques que tu obtiens au final, l'espèce de charabia... Bah c'est normal, ça correspond à  ce que je te disais, à  savoir que le format généré par les NSArchivers est un peu complexe, enfin c'est un format interne à  Apple pour stocker les graphes d'objets... au final c'est un XML mais de là  à  comprendre comment il est organisé... personnellement j'ai jamais cherché à  comprendre et on s'en fiche : ça génère un gros pâté de NSData, tu sauves ces NSData dans un fichier comme tu le fais, et plus tard tu relis ces NSData du fichier, tu les redécodes, et basta. Le format interne (la tambouille Apple) de représentation de tes données en NSData, c'est le souci du framework.

    Du coup, pour répondre à  ta question sur ton décodage, bien sûr que non ce n'est pas normal que tu n'utilises pas plistPath. Le principe comme je viens de le dire est de relire le contenu du fichier dans un NSData, puis d'utiliser un NSUnarchiver pour décoder lesdites data lues.
    Là  ton code initialise "data "avec un simple "alloc"+"init", donc tu crées un NSData vide... et ensuite tu crées un NSUnarchiver pour décoder... ce NSData vide. Forcément, en sortie tu ne risques pas d'obtenir grand chose.
    C'est un NSData contenant les octets du fichier à  lire qu'il faut décoder, pas un NSData vide. Donc quand tu crées ton NSData* data il faut l'initialiser avec le contenu du fichier ([tt]initWithContentsOfFile:[/tt] bien sûr.

    Et après comme te l'as indiqué Céroce, quand tu demandes à  un NSUnarchiver/NSCoder "decodeObjectForKey:", ne pas oublier de faire un "retain" sur le résultat, puisqu'il te retourne un objet autoreleased. (et bien sûr de balancer ce "retain" par un "release" dans le destructeur/dealloc de ta classe). Ou encore mieux, d'utiliser une [tt]@property(retain) NSArray* tableauPersonnes;[/tt] et la notation [tt]self.tableauPersonnes[/tt] pour manipuler ta propriété, plutôt que de manipuler une variable d'instance, comme ça ta propriété fait toute seule les retain/release à  chaque affectation.
  • CéroceCéroce Membre, Modérateur
    02:17 modifié #10
    Il faut utiliser un objet racine. Il s'agira pour toi de la liste des personnes.

    ça donne quelque chose comme ça:

    - (void) savePersonnes<br />{<br />	NSData *data = [NSKeyedArchiver archivedDataWithRootObject:personnes];<br />	NSError *error;<br />	BOOL success = [data writeToFile:[self personnesPListPath] options:0 error:&amp;error];<br />	if(!success)<br />		NSLog(@&quot;Error while saving personnes: %@&quot;, [error localizedDescription]);<br />}<br /><br />- (void) loadPersonnes<br />{<br />	NSError *error;<br />	NSData *data = [NSData dataWithContentsOfFile:[self personnesPListPath] options:0 error:&amp;error];<br />	if(data == nil)<br />		NSLog(@&quot;Error while loading personnes: %@&quot;, [error localizedDescription]);<br />	<br />	NSMutableArray *personnes = [NSKeyedUnarchiver unarchiveObjectWitFile:plistPath];<br />	[personnes retain];<br />}
    
  • AdamAdam Membre
    02:17 modifié #11
    Oui pour Plistpath j'ai trouvé la solution juste après avoir posté mon message.
    C'est pourquoi je me suis permis d'éditer.

    Merci Ceroce pour ce code, mais j'ai trouvé quelque chose qui semble bien fonctionner.


    Donc maintenant pour charger j'utilise cette méthode qui fonctionne (je retrouve bien le bon utilisateur) :
    <br /> //Recharge les données du Plist<br />&nbsp; &nbsp; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);<br />&nbsp; &nbsp; NSString *documentsDirectory = [paths objectAtIndex:0];<br />&nbsp; &nbsp; NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:@&quot;preferenceUtilisateur.plist&quot;];<br />&nbsp; &nbsp; NSMutableData *data = [[NSMutableData alloc] initWithContentsOfFile:plistPath];<br />&nbsp; &nbsp; NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];<br />&nbsp; &nbsp; [tableauUtilisateur removeAllObjects];<br />&nbsp; &nbsp; tableauUtilisateur=[unarchiver decodeObjectForKey:@&quot;utilisateur&quot;];<br />&nbsp; &nbsp; //tableau qui contient toute les valeurs<br />&nbsp; &nbsp; NSLog(@&quot;Apres la sauvegarde : %@ &quot;,tableauUtilisateur);<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; [unarchiver release];<br />&nbsp; &nbsp; [data release]; <br />
    


    Mais le problème si je clique sur une de mes cellules, j'ai un EXC_BAD, qui vient de plusieurs endroits (ça dépend des fois). Mais ça met toujours en erreur mon tableauUtilisateur...

    D'après ce que j'ai compris, ça pourrait être à  cause de retain, c'est ça ? pourtant je met juste le resultat dans mon tableauUtilisateur (qui est alloué au début dans le ViewDidLoad, et relâché à  la fin dans le dealloc), ce n'est pas bon ?
  • AdamAdam Membre
    02:17 modifié #12
    Ah oui c'était bien le retain qui manquait (mais je n'ai toujours pas compris pourquoi).
    Merci beaucoup pour votre aide.  :D
  • AliGatorAliGator Membre, Modérateur
    mai 2011 modifié #13
    dans 1306855265:

    Ah oui c'était bien le retain qui manquait (mais je n'ai toujours pas compris pourquoi).
    Alors arrête tout et vas lire toute la littérature sur la gestion mémoire en Cocoa.

    C'est un point crucial et incontournable, et si tu commences à  coder sans en avoir compris les principes et sans que ça devienne naturel tu vas droit dans le mur.
    Mieux vaut prendre un peu de temps pour comprendre les règles de gestion mémoire et après ne plus avoir de problème, que de coder sans trop les avoir comprises et faire des applications qui vont soit avoir des fuites mémoires et gonfler à  vue d'oeil, soit planter sans prévenir.

    Et à  ton code ça se voit qu'il te manque quelques éléments de base sur le sujet, car par exemple faire un removeAllObjects sur un tableau qu'au final tu ne vas plus jamais réutiliser après, puisque dans cette variable d'instance tableauPersonnes à  la ligne d'après tu remplaces l'objet précédent par un tout nouveau (celui que te rend decodeObjectForKey) donc l'objet qui était avant dans tableauPersonne va être perdu dans la nature et provoquer une jolie fuite mémoire dans ton application...

    Et sinon, ton code n'est pas conformer à  ce qui doit être fait pour gérer le rootObject de ton graphe d'objets que tu sérialises (en l'occurrence ton tableau de personnes). Reprend le code qu'a indiqué Céroce, car là  ton code s'il marche de prime abord car tu n'as sans doute pas de référence cyclique entre tes objets ni cas de boucles (genre personne1.mari = personne2 et personne2.femme = personne1 ==> boucle) donc t'as de la chance que ça déconne pas, mais c'est pas un pari à  faire pour avoir une appli stable ! En plus ton graphe d'objets Personnes que tu stockes n'a pas de racine officiellement définie si tu utilises un code comme le tien... Bref faut vraiment utiliser la méthode pour manipuler le rootObject, celle qu'a mis Céroce quoi.
  • AdamAdam Membre
    juin 2011 modifié #14
    Bonjour,
    Oui concernant la mémoire, c'est ce que je fais (mais j'ai toujours du mal à  comprendre)..

    Okay donc la méthode de "Ceroce" est beaucoup mieux que la mienne, je vais la reprendre.

    Merci beaucoup de votre aide, c'est rare les forums comme ça.
  • AdamAdam Membre
    02:17 modifié #15
    Je me permet de revenir sur ce post plutôt d'en créer un nouveau.

    J'ai bien suivi la méthode de "Céroce" pour sauvegarder et charger mes données.
    Mais à  la première utilisation de mon application, il n'y a pas encore de données inscrite et je fait un "load" dans la Plist.

    Il y a donc rien inscrit dans la Plist, et je load, ce qui me renvoi un NSLog : Error while loading personnes: The operation couldn't be completed. (Cocoa error 260.)
    Mais je ne peux pas enlever ce load, car une fois l'application a sauvegardé dedans, tout est OK.
    Est-ce que cela peut poser problème ?

    Merci
  • CéroceCéroce Membre, Modérateur
    02:17 modifié #16
    Regarde simplement si le fichier est présent.

    BOOL present = [[NSFileManager defaultManager] fileExistsAtPath:path];
    
  • AdamAdam Membre
    02:17 modifié #17
    Merci d'avoir repris le cours de la discussion.

    Ouai mais si il charge et qu'il y a rien dans le fichier, ça peut créer des problèmes ?
  • CéroceCéroce Membre, Modérateur
    02:17 modifié #18
    Ce cas me parait impossible. Même si ton objet "racine" ne contient aucun objet, le fichier enregistré ne sera pas vide, il devrait au moins indiquer la nature de cet objet racine.
  • AdamAdam Membre
    02:17 modifié #19
    Bon le code erreur est : Error while loading personnes: The operation couldn't be completed. (Cocoa error 260.)

    C'est lors du chargement à  cet endroit :
    <br />&nbsp; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);<br />&nbsp; &nbsp; NSString *documentsDirectory = [paths objectAtIndex:0];<br />&nbsp; &nbsp; NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:@&quot;preferenceUtilisateur.plist&quot;];<br />	NSError *error;<br />	NSData *data = [NSData dataWithContentsOfFile:plistPath options:0 error:&amp;error];<br />	if(data == nil)<br />		NSLog(@&quot;Error while loading personnes: %@&quot;, [error localizedDescription]);<br />	<br />	NSMutableArray *personnes = [NSKeyedUnarchiver unarchiveObjectWithFile:plistPath];<br />	[personnes retain];<br />&nbsp; &nbsp; [tableauUtilisateur release]; <br />&nbsp; &nbsp; tableauUtilisateur=personnes;<br />
    



    Mais ça le fait que à  la 1ere utilisation du programme. C'est à  dire quand il y a une TableView vide, aucun contact n'est encore présent. Mais un fois un contact créé, ce message n'apparait plus.


  • CéroceCéroce Membre, Modérateur
    02:17 modifié #20
    OUI C'EST NORMAL, SI LE FICHIER N'EST PAS LÀ, NSDATA N'ARRIVE PAS À LE LIRE !!!  >:)   >:)
Connectez-vous ou Inscrivez-vous pour répondre.