Save et Save As "à  la main"

HerveHerve Membre
mars 2011 modifié dans API AppKit #1
Bonjour,

Dans un projet, j'ai dû renoncer à  utiliser NSDocument parce que je ne pouvais utiliser le multi-fenêtrage. J'y crée des NSMenu (voir discussion à  côté).

J'ai donc fait ceci pour sauvegarder :
- (IBAction) sauvegardeDocument : (id) sender{<br />	[self sauveDessinEnCours];//méthode pour mettre dans mesDonnees le travail en cours, marche TB.<br />	int runResult;<br />	NSSavePanel * savePanel = [NSSavePanel savePanel]; <br />	//diverses optimisations de la fenêtre<br />&nbsp; &nbsp; &nbsp; &nbsp; runResult = [savePanel runModal];<br />	if (runResult == NSOKButton){<br />		[self setNomDeFichier :[savePanel filename]];//nomDeFichier est une String de la classe AppController<br />		[NSKeyedArchiver archiveRootObject:mesDonnees toFile:nomDeFichier ];<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //mesDonnees : instance d&#039;une classe stockant tout mon travail.<br />	}<br />}


et pour ouvrir cela :
<br />[self setNomDeFichier :[openPanel filename]];<br />mesDonnees = [NSKeyedUnarchiver unarchiveObjectWithFile:nomDeFichier]; <br />[mesDonnees retain];<br />

Cela marche très bien.
La NSString "nomDeFichier" a  @property et @synthesize, comme vous l'aviez compris.

Dans ma méthode Save (sans passer par le NSSavePanel donc), la méthode
[NSKeyedArchiver archiveRootObject:mesDonnees toFile:[self nomDeFichier ]];

refuse de marcher. J'ai pourtant bien instancié avec alloc et init le NSString dans la méthode init de la classe. Selon vous, pourquoi cela ne marche pas?

J'ai aussi vu, en bossant là  dessus la classe NSURL ainsi qu'une discussion dans ce forum à  ce sujet : j'ai essayé de sauvegardé un NSURL à  la place d'un NSString, sans succès pour l'instant. Le problème me semble venir du fait que je n'utilise pas NSSavePanel. Des vérifications avec NSLog montrent que "nomDeFichier" est bien conservé et porte bien tout le chemin vers l'archive.

J'ai passé déjà  plusieurs heures là  dessus, je sèche. Quelqu'un connaà®t peut-être le truc? (C'est maintenant une application à  une seule fenêtre).

Réponses

  • laudemalaudema Membre
    09:21 modifié #2
    Bonjour,

      Pas de soucis ici pour sauvegarder que ce soit dans une application Document based ou pas.
    <br />&nbsp; &nbsp; BOOL didIt = [NSKeyedArchiver archiveRootObject:@&quot;À archiver&quot; toFile:@&quot;/Users/ld/Desktop/aArchiver&quot;];<br />&nbsp; &nbsp; printf(&quot;didIt: %s&#092;n&quot;, didIt ? &quot;YES&quot;:&quot;NO&quot;);<br />&nbsp; &nbsp; //didIt: YES<br />
    

    Me sauvegarde sans souci un fichier qui pourra être désarchivé pour retrouver @À archiver plus tard. Pour que ça ne marche pas, même si rootObject est à  nil ça marche, il faut donc que mon chemin ne soit pas valide.
    Tu devrais peut être regarder par là  (et les droits en écriture) à  vérifier avec NSFileManager et isWritableFileAtPath: ...
    Avec les NSKeyedArchiver on a peu de souci justement, c'est parfois plus compliqué quand on veut utiliser directement writeToFile avec un tableau ou un dictionnaire (on ne peut mettre que des objets qui sont "all property list objects (NSString, NSData, NSArray, or NSDictionary objects)"
    hth
  • HerveHerve Membre
    09:21 modifié #3
    Merci Laudema,

    Il faudra vraiment que je m'y fasse à  la gestion de la mémoire. Là  encore, je n'avais pas mis un petit "retain" qui fait tout. Aussi le NSLog fonctionnait bien dans la méthode "ouvrir" mais plus une fois en dehors. J'ai lu la doc, mais il y a encore du chemin. J'ai d'ailleurs des problèmes parfois à  l'ouverture, etc. Que c'est dur!

    Merci, problème résolu donc. Ahhhhh!
  • laudemalaudema Membre
    09:21 modifié #4
    dans 1299430254:

    Que c'est dur!

    La pente est raide mais une fois arrivé sur le premier plateau ça va beaucoup mieux ;)
    Et le panorama peut valoir le coup d'oeil (je manque d'éléments de comparaison).
    Mais pour remplir la chaudière il faut des objets et ceux ci ont une vie qu'il faut assimiler, n'hésite pas à  revoir souvent les simples principes jusqu'à  ce qu'ils deviennent une seconde nature. C'est quand même pas si dur : new, alloc, retain, copy tu dois relacher quand tu n'en as plus besoin. Pour les autres tu laisses faire Cocoa.. Un objet créé par une méthode de classe ne vit que dans la méthode en cours sauf s'il est retenu par un autre (c'est souvent là  que ça coince: quand l'autre disparaà®t..).
    Utilise le débugger pour suivre la référence de tes objets à  l'intérieur de la méthode en exécution ( ! 0x0 ).
    Et utilise Instruments pour voir si tu oublies d'en relâcher ou, cas inverse, quand ton appli plante sur un "EXC_BAD_ACCESS" qui signe souvent un objet parti quand on compte sur lui.
    Enfin les propriétés (retain | copy) et @synthesize peuvent aider à  bien gérer ça mais il ne faut pas oublier de remplir la méthode dealloc en fonction de ce que tu as choisi comme type de propriété. Et utiliser self.monObjetDeClasse = monNouvelObjet à  chaque fois.

    Si tu veux te rafraà®chir la mémoire ;-)
  • HerveHerve Membre
    09:21 modifié #5
    dans 1299445906:

    Un objet créé par une méthode de classe ne vit que dans la méthode en cours sauf s'il est retenu par un autre (c'est souvent là  que ça coince: quand l'autre disparaà®t..).
    Utilise le débugger pour suivre la référence de tes objets à  l'intérieur de la méthode en exécution ( ! 0x0 ).


    Là  par exemple, j'avais pas bien compris...  :)

    A propos de la méthode "autorelease", si j'ai bien compris la doc, il faut lancer quelque part un "NSAutoreleasePool". Est-ce une méthode qu'il faut appeler lors de la manipulation du programme, ou est-ce qu'elle gère le truc tout seul? Je comprends qu'il faut, lorsqu'on aura utilisé les données en "autorelease" lancer le "NSAutoreleasePool" à  un moment. Hum...  >:(

    J'ai aussi du mal à  comprendre les "Performance tools". L'outil "Allocation" par exemple fait des beaux graphes bleus, mais pour s'y retrouver... Ou bien cela sert simplement à  vérifier que les objets se créent puis disparaissent bien "globalement". Enfin, à  chaque jour ses progrès, je saurai faire ceux-là  aussi...

    Ceci dit, Cocoa + XCode + NextStep + CoreImage (et bientôt + CoreAudio), ça dépote!
  • DrakenDraken Membre
    09:21 modifié #6
    Oui, pour faire des objets autoreleasés, il faut avoir un NSAutoreleasePool quelque part. Comme les choses sont bien faites, le système en gère un par défaut. Tu n'as pas besoin de t'en préoccuper, ça marche tout seul !

    Ouvre le fichier main.m de n'importe quelle application si tu veux voir la déclaration du pool.

    Sur ce ==> dodo pour moi !

  • laudemalaudema Membre
    09:21 modifié #7
    dans 1299536106:


    Là  par exemple, j'avais pas bien compris...  :)

    A propos de la méthode "autorelease", si j'ai bien compris la doc, il faut lancer quelque part un "NSAutoreleasePool". Est-ce une méthode qu'il faut appeler lors de la manipulation du programme, ou est-ce qu'elle gère le truc tout seul? Je comprends qu'il faut, lorsqu'on aura utilisé les données en "autorelease" lancer le "NSAutoreleasePool" à  un moment. Hum...  >:(

    Tu as compris la doc mais tu ne l'as peut être pas lue assez souvent encore  :o
    La phrase que j'ai mis du temps à  trouver mais qui m'a fait l'impression d'avoir trouvé l'interrupteur dans le noir:

    The Application Kit automatically creates a pool at the beginning of an event cycle (or event-loop iteration), such as a mouse down event, and releases it at the end, so your code normally does not have to worry about them

    Dans le lien que je te donnais, onglet AutoreleasePool

    Quand tu cliques sur un bouton pour lancer une méthode AppKit va créer son bassin et quand tu sortiras de la méthode tous les objets que tu auras créés disparaitront si tu n'as pas prévu (dans ton code) de les garder. Par exemple il va pouvoir afficher ton tabeau de chaà®nes mais s'il doit le redessiner (et il redessine souvent sans qu'on s'en aperçoive, cliquer sur la fenêtre peut suffire) ça crashe !

    dans 1299536106:

    J'ai aussi du mal à  comprendre les "Performance tools". L'outil "Allocation" par exemple fait des beaux graphes bleus, mais pour s'y retrouver... Ou bien cela sert simplement à  vérifier que les objets se créent puis disparaissent bien "globalement". Enfin, à  chaque jour ses progrès, je saurai faire ceux-là  aussi...
    Ceci dit, Cocoa + XCode + NextStep + CoreImage (et bientôt + CoreAudio), ça dépote!


    Je t'accorde qu'Instruments n'est pas simple. Je m'en sers surtout pour trouver les zombies auquel cas il repasse au premier plan avec une bulle à  l'endroit où le programme a crashé qui porte l'adresse mémoire de l'objet défaillant que tu peux retrouver en double cliquant sur l'étiquette qui contient cette adresse ou en filtrant comme il est montré ici Descendre jusque "Searching in the Detail Panel" (dans une doc toujours plutôt copieuse ;/

    Pour le lancer il suffit de choisir "Run with Performance Tool" du menu "Run" et "Zombies" dans le sous menu.
    Attention tu perds tes logs dans la console Xcode (mais tu les as dans la console qui est dans le sous dossier Utilitaires du dossier Applications)
  • HerveHerve Membre
    09:21 modifié #8
    Merci pour toutes ces informations. Merci de me rassurer en particulier en ce qui concerne le "Autorelease pool". Cela me rappelle un peu le "garbage collector", mais finalement en mieux... Je vais lire aussi la doc concernant les outils  de diagnostique, nouveaux pour moi.

Connectez-vous ou Inscrivez-vous pour répondre.