Problème pour insérer un NSDocument (a MVC problem)

HerveHerve Membre
13:12 modifié dans API AppKit #1
Bonjour,

Je reviens vers vous suite sans doute à  une nouvelle erreur de conception.

J'ai plusieurs NSView sur mon projet qui fonctionnent très bien ensemble. Super. Je voudrais stocker des valeurs que les unes ou les autres conservent dans leurs classes respectives : des positions, des points, des rectangles, en fait des valeurs de base, float, bool, etc.

J'ai bien implémenté NSCoding partout, tout va bien. J'ai fait une classe dérivée de NSDocument dans XCode et reliée aux NSView par des IBOutlet dans Interface Builder. La méthode "Save" semble fonctionner : lorsque je lis avec "Text Edit" l'archive, les valeurs semblent bien archivées.

Le problème est que l'objet de type NSDocument dans Interface Builder n'offre pas les entrées "open" et "new" mais seulement "save" ou "save as". J'ai tenté d'ajouter une classe NSDocumentController mais sans succès. Les "open" et "new" de "First Responder" demeurent sans effet. (J'ai connecté les "save" et "save as" du menu à  ma classe NSDocument pour que cela marche). Il n'y a pas d'id pour la méthode :



- (BOOL) readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError<br />



Où est-ce que j'ai faux?? J'ai pas trouvé... :(
«1

Réponses

  • mpergandmpergand Membre
    13:12 modifié #2
    Salut,

    Il n'y a rien a connecter !
    Tu dois implémenter, pour la lecture, une des méthodes:

    - (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
    ou
    - (BOOL)readFromURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError

    et pour la sauvegarde:
    - (BOOL)writeToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError
    ou
    - (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError

    Lorsque tu fais un new, les méthodes readFrom... ne sont pas appelées, c'est dans:
    - (void)windowControllerDidLoadNib:(NSWindowController *) aController

    que tu dois déterminer s'il s'agit d"un document vide (data==nil, par ex)
  • HerveHerve Membre
    13:12 modifié #3
    Merci mpergand. En fait, en re-re-relisant la doc sur les "document based application", j'ai compris le rôle des NSDocumentController : il ne fallait surtout pas y mettre
    - (BOOL) readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
    
    ...

    J'ai bien mis les deux méthodes dont tu parles dans le NSDocument. Problème, "first responder" ne les appelle pas. En re-relisant cette doc, je me suis dit que j'avais peut-être eu tord de donner un autre nom que "MyDocument" à  cette classe. J'ai refait avec ce nom : toujours rien...

    Mon problème est la communication entre le menu et le NSDocument. Le reste devrait marcher ensuite... Je peux connecter "save" mais pas "open". Je m'y prends donc mal. Je ne sais pas encore pourquoi...  >:(
  • laudemalaudema Membre
    13:12 modifié #4
    dans 1296983167:

    Mon problème est la communication entre le menu et le NSDocument.[/u] Le reste devrait marcher ensuite... Je peux connecter "save" mais pas "open". Je m'y prends donc mal. Je ne sais pas encore pourquoi...  >:(

    Bonjour Hervé

    Le menu a son nib, relié à  l'application pas au document.
    Tu pourras peut être trouver un peu plus de renseignements sur ce que tu veux faire ici:
    http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/Documents/Documents.html
    Et aussi ici
    http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/AppArchitecture/Concepts/DocumentArchitecture.html
    Ta sous classe de NSDocument est aussi le WindowController du MyDocument.xib

    hth
  • HerveHerve Membre
    février 2011 modifié #5
    dans 1296987309:


    Ta sous classe de NSDocument est aussi le WindowController du MyDocument.xib



    Oui, sauf que MyDocument.xib n'existe pas. Il n'y a dans mon projet pour l'instant qu'un MainMenu.xib... Le fait d'avoir créé mon dérivé de NSDocument après avoir avancé dans mon projet pose t-il problème?

    Je vais aussi essayer de trouver comment créer ce MyDocument.xib, mais j'ai l'impression qu'il se fait automatiquement lorsque l'on crée un certain type de projets. Il fautdrait que ce MyDocument.xb puisse aussi être relié aux CustomViews sur la fenêtre qui sont, elles, dans le MainMenu.xib (of course). L'architecture du projet semble avoir été mal conçue au départ, non? (c'est mon premier projet Cocoa, j'ai une excuse!...) ??? Plusieurs CustomView dans la fenêtre de l'application doivent en effet pouvoir communiquer avec MyDocument.

    Tant qu'on y est, peut-on travailler avec deux menus? (et qu'un seul apparaisse lors de l'exécution bien sûr) : j'ai besoin d'ajouter des items au mien. Bref, comment MyDocument.xib et MainMenu.xib vont-ils travailler ensemble?

    Sinon, j'envisageais une dérivation de NSDocumentController comme la doc le suggère. Quelle méthode dois-je employer pour qu'il lance la procédure (BOOL) readFromData de MyDocument selon vous?

    J'essaie là  à  partir de AppController une méthode de sélection de fichier. La méthode (BOOL) readData... a en dernier argument une NSError que je n'ai pas la possibilité d'instancier. Comment puis-je appeler cette méthode selon vous?

    Bref, j'suis un peu perdu là ...
  • mpergandmpergand Membre
    13:12 modifié #6

    Oui, sauf que MyDocument.xib n'existe pas. Il n'y a dans mon projet pour l'instant qu'un MainMenu.xib...


    Tu m'étonnes que ça marche pas B)

    Comme tu es un débutant, le mieux est que tu crées un nouveau projet de type Document (document based) et que tu y ajoutes  les classes de ton ancien projet (document, views, etc), ensuite il te faut modifier les fichiers xib ( noms des classes, outlets, etc)


    Tant qu'on y est, peut-on travailler avec deux menus? (et qu'un seul apparaisse lors de l'exécution bien sûr) : j'ai besoin d'ajouter des items au mien. Bref, comment MyDocument.xib et MainMenu.xib vont-ils travailler ensemble?


    Laisse tomber  :)



    Sinon, j'envisageais une dérivation de NSDocumentController comme la doc le suggère. Quelle méthode dois-je employer pour qu'il lance la procédure (BOOL) readFromData de MyDocument selon vous?


    idem ...


    Pour l'instant, il te faut repartir sur des bases saines, c à  d un projet de type document based fonctionnel.
    Pour ajouter des entrées de menus, on verra après  ;)
  • laudemalaudema Membre
    13:12 modifié #7
    +1

    Finalement si ta question était: Comment insérer une sous classe de NSDocument dans une application non "Document based" quand on débute (et même longtemps après d'ailleurs). La réponse serait: ne le fais pas, implémente une méthode "save:" dans ton AppController (reliée à  l' item de menu éponyme dans MainMenu.xib) où tu sauveras les données mises dans un dictionnaire (ou un tableau) à  l'aide de "writeToFile:atomically:" de NSDictionary http://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/Reference/Reference.html#//apple_ref/occ/instm/NSDictionary/writeToFile:atomically: et une méthode open: qui utilisera dictionaryFromFile: pour le reconstituer. Mieux encore avec URL plutôt que File ( dictionaryFromURL: et writeToURL: )

    Ou donc comme le dis mpergand tu refais une application en cochant l'option "document-based" dans le dialogue que Xcode te présente quand tu fais New Project dans le menu File..
  • HerveHerve Membre
    13:12 modifié #8
    J'en arrivais à  cette conclusion, merci à  tous les deux.

    En mettant "nil" à  error dans l'appel de la méthode, et des NSLog en console, cela avait "l'air de passer", mais mon petit doight me dit que ce n'est pas une méthode sure.

    D'ailleurs, la méthode
    NSOpenPanel *op = [NSOpenPanel openPanel];<br />	<br />	[op beginSheetForDirectory:nil<br />						&nbsp; file:nil<br />						 types:nil<br />				modalForWindow:[leDessin window]<br />				 modalDelegate:self<br />				didEndSelector:@selector(documentChoisi:returnCode:contextInfo:)<br />				&nbsp;  contextInfo:nil];
    


    semble devoir devenir obsolète d'après la doc dans les nouveaux OS. C'est dommage car je voulais m'en servir pour importer des images ou des sons par exemple dans des classes. Rien dans la doc n'indique une "méthode remplaçante".

    Bon, mercredi, je devrais avoir le temps de refaire mon projet. Je vous tiendrai au courant. Merci encore.
  • laudemalaudema Membre
    13:12 modifié #9

    la méthode
    <br />	[op beginSheetForDirectory:nil<br />						&nbsp; file:nil<br />						 types:nil<br />				modalForWindow:[leDessin window]<br />				 modalDelegate:self<br />				didEndSelector:@selector(documentChoisi:returnCode:contextInfo:)<br />				&nbsp;  contextInfo:nil];
    


    semble devoir devenir obsolète
    Rien dans la doc n'indique une "méthode remplaçante".


    beginSheetForDirectory:file:types:modalForWindow:modalDelegate:didEndSelector:contextInfo:
    (Deprecated in Mac OS X v10.6. Use beginSheetModalForWindow:completionHandler: instead. You can set absoluteDirectoryPath using setDirectoryURL:, and you can set fileTypes using setAllowedFileTypes:.)

    beginSheetModalForWindow:completionHandler:
    http://developer.apple.com/library/mac/documentation/cocoa/Reference/ApplicationKit/Classes/NSSavePanel_Class/Reference/Reference.html#//apple_ref/doc/uid/20000309-SW23 Sur le site Apple mais elle est aussi dans la documentation Xcode.
    Avec quelques liens pour des applications de code démo que je te laisse découvrir, avec les blocks probablement mais ça vaut le détour amha

  • HerveHerve Membre
    13:12 modifié #10
    Bon, j'ai progressé et ai refait mon projet avec un "Document Based application". Cela marche à  peu près bien sauf qu'à  l'ouverture du fichier, je n'arrive pas à  transmettre au NSView mon tableau.

    Celui-ci est bien archivé et ouvert (vérification en console, voir code) mais "leDessin" ne reçoit pas l'info.

    Voici le code en question :
    - (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError<br />{<br />	NSLog(@&quot;OuvertureArchive de type %@&quot;, typeName);&nbsp;  //s&#39;affiche<br />	NSMutableArray *newArray = nil;<br />	@try{<br />		newArray = [NSKeyedUnarchiver unarchiveObjectWithData : data];<br />	}<br />	@catch (NSException *e) {<br />		if (outError){<br />			NSDictionary *d = [NSDictionary dictionaryWithObject:@&quot;Les données sont corrompues.&quot; <br />														&nbsp; forKey:NSLocalizedFailureReasonErrorKey];<br />			*outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:d];<br />		}<br />		return NO;<br />	}<br />	NSLog(@&quot;Sortie bloc try&quot;); //s&#39;affiche bien<br />	NSLog(@&quot;newArray : %@&quot;,[newArray description]);//la description s&#39;affiche bien identique à  celle faite au moment de l&#39;archive.<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;  //la figure est donc bien correctement archivée.<br />	[leDessin setLesFigures:newArray];//le message ne passe pas alors que la communication entre MyDocument <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; //et leDessin par ailleurs fonctionne très bien.<br />	return YES;<br />		<br />}
    


    la méthode en question est des plus simples :
    - (void) setLesFigures: (NSMutableArray *) unDessin{<br />	NSLog(@&quot;appel setLesFigures&quot;);	//ce message ne s&#39;affiche pas en console, aussi cette méthode n&#39;est pas appelée.<br />	[lesFigures release];<br />	lesFigures = [[NSMutableArray alloc] initWithArray:unDessin];<br />	[self setNeedsDisplay:YES];<br />}<br />
    


    Est-ce que la méthode de lecture doit demeurer "en interne" si j'ose dire? Avez-vous une explication, d'autant plus, une fois encore, que la communication entre les deux classes est excellente par ailleurs.

    Merci encore pour vos précieux conseils qui m'ont fait faire depuis mon premier message d'infinis progrès.
  • mpergandmpergand Membre
    février 2011 modifié #11
    Une piste:
    <br />NSLog(@&quot;newArray : %@&quot;,[newArray description]);//la description s&#39;affiche bien identique à  celle faite au moment de l&#39;archive.<br />	<br />NSLog(@&quot;leDessin : %@&quot;, leDessin);<br />
    


    :)
  • laudemalaudema Membre
    13:12 modifié #12
    Une autre piste, puisque ta question comportait (MVC) : où est le MVC là  dedans ?
    Si tu utilises le keyValueCoding il faudrait voir par là  si ta vue a bien une propriété lesFigures. Si c'est le cas alors tu devrais pouvoir faire
    self.leDessin.lesFigures = newArray;
    
    . En faisant de cette manière la vue leDessin qui est affichée sera informée que la valeur de sa propriété lesFigures a changé et d'adaptera à  la nouvelle donne.
    Sinon j'irais quand même voir la méthode setLesFigures: pour voir s'il est demandé à  la vue de se redessiner quand son tableau d'objets internes a changé.
    En supposant que je ne me sois pas trompé sur tes intentions..
  • HerveHerve Membre
    13:12 modifié #13
    Merci Laudema,

    Le problème semble plutôt venir du fait que ma NSView "leDessin" n'est pas instanciée lors de l'ouverture de l'archive. J'ai fait un test avec NSLog sur des valeurs par défaut : elles sont toutes à  0 alors qu'elles ne le devraient pas.

    Pour mieux vous expliquer ce que je fais, j'utilise des méthodes mouseDown, Dragged, etc. dans le NSView pour faire des dessins. Une classe "Figure" stocke des valeurs de base (int, NSRect, etc.) que le "drawRect" de "Dessin" interprète pour faire la figure. Cela marche maintenant très bien.

    Mon idée était de stocker dans "MyDocument" plusieurs dessins à  terme, le dessin en cours étant dans la NSView, et les autres dans "MyDocument" (ceci pour éviter des aller/retours entre les classes lors du dessin avec les méthodes "mouse"). La méthode "getDessin" marche, la méthode "setDessin" intègre bien "[self setNeedsDisplay:YES];"

    J'ai essayé aussi une méthode d'appel de set en dehors du bloc "BOOL readFromData..." sans succès. Normal, puisque apparemment, comme le logiciel ouvre un nouveau document, "leDessin" n'existe pas encore. Bon, encore un truc que je n'ai pas compris...
  • mpergandmpergand Membre
    février 2011 modifié #14

    J'ai fait un test avec NSLog sur des valeurs par défaut : elles sont toutes à  0 alors qu'elles ne le devraient pas.


    Et oui, c'est normal  :P

    Le fichier ressources n'est pas encore chargé, puisque qu'il le sera dans:
    - (void)windowControllerDidLoadNib:(NSWindowController *) aController

    Dans readFromData tu dois mémoriser data dans une variable d'instance et dans windowControllerDidLoadNib:, mettre à  jour tes vues avec ces data.

    La méthode windowControllerDidLoadNib: de NSDocument est le pendant de awakeFromNib pour NSView.

    [EDIT]

    En fait, c'est le NSArray que tu dois recupérer:
    <br />- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError<br />{<br />	<br />	@try{<br />		newArray =[ [NSKeyedUnarchiver unarchiveObjectWithData : data] retain];&nbsp; // variable d&#39;instance<br />	}<br />	@catch (NSException *e) {<br />		if (outError){<br />			NSDictionary *d = [NSDictionary dictionaryWithObject:@&quot;Les données sont corrompues.&quot; <br />														&nbsp; forKey:NSLocalizedFailureReasonErrorKey];<br />			*outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:d];<br />		}<br />		return NO;<br />	}<br /><br />	return YES;<br />		<br />}
    
  • HerveHerve Membre
    13:12 modifié #15
    Absolument mpergand, effectivement la méthode passe si elle est employée dans "windowControllerDidLoadNib". Bon, elle ne veut pas marcher encore. Mais je vais maintenant peut-être essayer autre chose qu'une méthode "set". "Dessin" implémente NSCoding, voyons voir par là .

    Mais c'était bien ça. Merci beaucoup.
  • HerveHerve Membre
    février 2011 modifié #16
    Avec NSCoding, le dessin semble bien archivé (apparition des composants, couleurs etc. lors d'un contrôle via textEdit), "leDessin" est bien instancié, la méthode "encodeWithCoder:" est bien appelée, mais bien évidemment le dessin ne s'affiche pas, avec ou sans "setNeedsDisplay:YES" dans l'initialisation.

    J'ai une fuite de mémoire après le décodage.

    Cette méthode :
    NSLog(@&quot;leDessin : %@&quot;,[lesFigures description]);
    

    affiche bien en console les figures dans la méthode "initWithCoder", mais n'affiche plus rien dans la méthode "drawRect". Le tableau s'envole à  peine créé. J'ai bien fait pourtant :
    [super initWithCoder:c];<br />lesFigures = [[c decodeObjectForKey:@&quot;lesFigures&quot;]retain];
    


  • laudemalaudema Membre
    13:12 modifié #17
    Bonjour Hervé,
    En général la nuit porte conseil, je suis sûr qu'aujourd'hui ça va marcher ;)

    1) La méthode initWithCoder retourne nil appelée sur super.
    2) Peut être utiliser les propriétés si tu veux faire du MVC
    <br />if ( ! ( self = [super init] ) )<br />&nbsp; &nbsp; return nil;<br />self.lesFigures = [c decodeObjectForKey:@&quot;lesFigures&quot;];<br />
    

    Les messages à  nil sont parfaitement acceptés dans Objective-C et ils retournent nil..
  • HerveHerve Membre
    13:12 modifié #18
    Merci Laudema, mais j'ai du mal à  te comprendre. Que veux-tu dire par "retourne nil" : aucune mémoire n'est allouée à  l'objet? Doit-on allouer dans MyDocument l'espace mémoire? Pourtant la fenêtre du document s'affiche par ailleurs... J'ai mis des "retain" aussi bien dans la méthode "read data" que dans celle initWithCoder.

    J'ai lu plusieurs fois la doc Apple sur les documents (celle que tu m'avais dite plus une autre développant et comparant sérialisation et NSCoding, plus mon bouquin, plus les docs sur les classes concernées, etc.), je ne vois pas quelle propriété de MVC je n'ai pas faite.

    En particulier, MyDocument archive bien mes données, initWithCoder les retrouve bien (vérification en console avec des descriptions), mais ensuite les valeurs disparaissent quelque part... Il y a donc bien un problème d'allocation mémoire, mais où?
  • laudemalaudema Membre
    13:12 modifié #19
    Je veux dire que si tu fais
    <br />self = [super initWithCoder:c];<br />
    


    À la sortie self est égal à  nil ce qui n'empêche que si tu fais
    <br />[self setLesFigures:myArray];<br />//ou<br />lesFigures = myArray;<br />
    


    Ton appli ne bronchera pas seulement tout sera nil et donc rien ne pourra être dessiné dans ta vue. Tu dois faire
    <br />self = [super init];<br />lesFigures = [[ c decodeObjectForKey:@&quot;lesFigures&quot;] retain];<br />
    


    Ou mieux si tu as déclaré la propriété lesFigures en retain
    <br />self.lesFigures = [c decodeObjectForKey:@&quot;lesFigures&quot;]:<br />
    
  • HerveHerve Membre
    13:12 modifié #20
    Bon, ça commence à  marcher. Encore des problèmes mais bon, cela va mieux.

    J'ai fait une classe NSObject qui conserve toutes les données du projet et qui ensuite renvoie via MyDocument les valeurs au NSView. Comme quoi, pour que ça marche, il faut suivre le guide...

    Encore deux ou trois problèmes :
    - j'ai fait un NSMutableArray avec des NSFloat. Comment récupérer les "float" des tableaux? J'ai essayé plusieurs trucs sans succès. Il semble que le tableau pouvant porter toutes sortes d'objets, le compilateur refuse le basic
    "return [leTableau objectAtIndex:i]", et même le "return [NSNumber numberWithFloat:[leTableau objectAtIndex:i]]".
    Bon, ce devrait être facile à  résoudre.

    - une partie des données arrive bien, pas encore le reste. Bon, on va chercher.

    Mais ça commence à  marcher. Merci laudema et mpergand pour vos conseils précieux.

    C'est super sinon le protocole NSCoder. Ce serait dommage de faire sans... C'est mieux que les sérialisations que je faisais en Java en tous les cas. Quand je saurai vraiment faire (...) ce sera un apport précieux.
  • mpergandmpergand Membre
    13:12 modifié #21
    j'ai fait un NSMutableArray avec des NSFloat. Comment récupérer les "float" des tableaux?


    NSArray, NSDictionary, etc, n'acceptent que des objets, il faut donc créer un NSNumber pour tes floats:
    [aArray addObject:[NSNumber numberWithFloat: floatValue]];

    Pour récupérer cette valeur, il faut faire l'opération inverse:
    float f=[[aArray objectAtIndex:i] floatValue];
  • laudemalaudema Membre
    13:12 modifié #22
    dans 1297358800:


    C'est super sinon le protocole NSCoder. Ce serait dommage de faire sans... C'est mieux que les sérialisations que je faisais en Java en tous les cas. Quand je saurai vraiment faire (...) ce sera un apport précieux.


    Il y a peut être mieux que NSCoder: Core Data mais il vaut mieux pour l'utiliser avoir bien intégré les bindings et donc les protocoles NSKeyValueCoding et NSKeyValueObserving.

    Sinon pour tes floats le C est aussi disponible et tu peux déclarer et utiliser des tableaux C si tu préfères. Pour les archiver c'est plus complexe car on peut archiver une variable seule sans souci par les méthodes appropriées encodeFloat:forKey: c'est pour stocker des tableaux que ça coince car il faut tenir compte de la plateforme PPC ou x86 et du petit et gros bout des octets enregistrés http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/Archiving/Articles/codingctypes.html
  • HerveHerve Membre
    13:12 modifié #23
    dans 1297371431:


    Il y a peut être mieux que NSCoder: Core Data mais il vaut mieux pour l'utiliser avoir bien intégré les bindings et donc les protocoles NSKeyValueCoding et NSKeyValueObserving.



    Oui, j'essaierai de maà®triser cela ensuite.

    Toujours en ce qui concerne l'architecture d'une appli Cocoa, la gestion de la fenêtre dans ce que je fais est la classe MyDocument qui gère donc les actions boutons à  l'écran + save et open. Tout le reste, ce sont d'autres classes qui s'en chargent. Vaut-il mieux que je sépare dans une "AppController" la gestion des boutons? Je crains alors que les outlets et IBAction n'apparaissent plus comme maintenant dans IB dans FileOwner, il me faudrait alors sans doute mettre un Object relié à  AppController. Cela n'a rien de grave me direz-vous, mais n'est-ce pas plus complexe pour rien? (la doc semble indiquer qu'il faut bien séparer les trois)

    Je me pose la question parce que ma sauvegarde commence à  marcher, mais pas très bien encore. En particulier, si je ferme une fenêtre lorsqu'il y en a deux d'ouvertes, ça turbine à  mort et ça plante au final... Pourtant, j'ai bien mis des méthode dealloc partout. Bon, je cherche un peu mais si vous savez pourquoi en général une appli plante lorsqu'on ferme une fenêtre et pas l'autre, je suis preneur.
  • HerveHerve Membre
    13:12 modifié #24
    Connaà®triez vous une doc qui explique bien les problèmes d'allocations de la mémoire et de release/dealloc?

    Mon problème lors de l'ouverture de mes fichiers vient bien de là . En Java, avec le ramasse-miettes, on n'apprend pas ça... (Ou bien j'utilise le ramasse miettes, mais bon...) Plus je corrige le problème, mieux ça marche d'ailleurs...

    Si j'ai bien compris, pas de release pour les valeurs de bases (en C, de type float, int, BOOL), mais il les faut pour tous les NSQuelqueChose. De même pour alloc/init. Y a t-il quelque chose à  comprendre en plus?
  • mpergandmpergand Membre
    13:12 modifié #25
    dans 1297455904:

    Connaà®triez vous une doc qui explique bien les problèmes d'allocations de la mémoire et de release/dealloc?


    http://cocoadev.com/index.pl?MemoryManagement
    http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html

  • AliGatorAliGator Membre, Modérateur
    13:12 modifié #26
    Les choses importantes à  savoir se résument en fait assez bien :
    • Tout alloc, un retain ou un ...copy (copy, mutableCopy, ...), doit être balancé par un release ou un autorelease.
    • C'est celui qui fait le alloc/retain/copy qui a la responsabilité de faire ce release/autorelease : celui qui crée ou retient un objet est responsable de cet objet et a la charge de le libérer.
      Ainsi, si tu fais un "alloc" sur une variable locale à  une fonction, il faut faire un "release" ou "autorelease" avant de sortir de cette fonction. Si tu fais un alloc sur une variable d'instance dans le constructeur, il faut faire le release dans le destructeur, etc.
    • Les constructeurs de commodité, c'est à  dire ceux dont le nom est construit à  partir du nom de la classe ("stringWithFormat" pour NSString par exemple) sont équivalent à  alloc+init+autorelease. Corrolaire, que l'on peut aussi déduire de la première règle puisqu'ils n'ont pas alloc/retain/copy dans leur nom, il n'y a pas à  les balancer par un release ou quoi (c'est déjà  fait).


    Un outil qui ne résoud pas tout mais aide quand même beaucoup, c'est d'utiliser "Build & Analyze" dans le menu Build de Xcode, pour demander au compilo d'analyzer ton code et de chercher et te monter les erreurs (en particulier de gestion mémoire) que tu as pu faire dans ton code. Ca ne les relève pas forcément toutes, mais celles qu'il relève sont en général tout à  fait justifiées et donc à  corriger.
  • HerveHerve Membre
    13:12 modifié #27
    Merci beaucoup, je vais suivre tes conseils AliGator. Le problème se pose dans mon cas : le dessin s'affiche mais ne peut être modifié. Les instances de classe disparaissent dès modification de quoi que ce soit, je n'ai donc pas bien fait ma récupération. Avec un peu de logique et tes conseils, cela devrait être résolu ce jour...  B)
  • HerveHerve Membre
    13:12 modifié #28
    Parfois l'esprit s'éclaire...

    une redéfinition de la méthode set de mon tableau au lieu d'en faire plusieurs qui se téléscopent :
    - (NSMutableArray *) lesFigures{<br />	return lesFigures;<br />}<br /><br />- (void) setLesFigures:(NSMutableArray *)unDessin{<br />	[lesFigures release];<br />	lesFigures = [[NSMutableArray alloc ]initWithArray:unDessin];<br />}<br />
    


    et voilà !
    /*problème résolu*/

    Ouf, ça a été dur!!  :o

    Merci à  tous! :)
  • AliGatorAliGator Membre, Modérateur
    13:12 modifié #29
    En fait les règles que j'ai citées se retrouvent dans la doc citée déjà  plus haut, sur cette page

    Sinon petite précision, attention quand tu fais un "setter" comme setLesFigures. Tu as un cas particulier qu'il faut penser à  traiter, c'est si tu passes comme argument... le tableau lesFigures actuel. Ce qui arrive rarement volontairement/directement, mais peut arriver indirectement de proche en proche par exemple. Dans ce cas tu risques de faire un release sur ton objet avant d'avoir fait un alloc/init (ou un retain) vu que unDessin et lesFigures seraient alors le même objet dans ce cas particulier.

    La solution, là  encore, est donnée toujours dans la même doc, sur cette page
  • laudemalaudema Membre
    13:12 modifié #30
    Sais tu que tu aurais aussi pu t'épargner ces lignes de code avec un @synthetize lesFiguresdans; le .m et un @property NSMutableArray* lesFigures; dans le .h ?
    En fait si on indique rien après @property Xcode considère qu'on a mis (assign) ce qui semble être ton cas puisque tu ne fais pas de retain.
  • HerveHerve Membre
    13:12 modifié #31
    J'avais bien fait dans un premier temps @property (avec assign. ) et @synthesize pour les tableaux, mais cela ne marchait pas. Sans doute à  cause alors d'autres méthodes d'initialisation faites "en concurrence" en quelques sortes. J'avais aussi essayé des  trucs genre des boucles réinitialisant un à  un les éléments du tableau. Ce qui surprend un peu en Cocoa, c'est qu'il n'y en a pas besoin. Dans d'autres classes (celle qui archive le document en particulier) je me suis contenté des @property";. Sans doute ai-je craint que la mémoire ne soit pas désallouée et l'objet totalement recréé...

    L'idée de AliGator aussi est intéressante. La communication n'aura lieu qu'entre deux classes. On devrait pouvoir empêcher cela.

    Cela ne m'effraye pas de "galérer" au début. Je pense qu'il n'y a pas d'autre moyen pour apprendre. L'utilisation des classes, en Java comme en Cocoa d'ailleurs, nécessite de se pencher constamment sur la doc, et il faut se tromper dans l'utilisation des méthodes les premières fois. Après, il y a un répertoire de classes que l'on utilise constamment et que l'on sait "par coeur".

    Mon premier projet, qui reprend en Cocoa mais en plus abouti grâce à  CoreImage un truc Java, avance bien. On commence à  voir le bout... J'ai vu qu'il y avait des endroits dans le forum pour présenter son travail, j'en parlerai une fois fini
Connectez-vous ou Inscrivez-vous pour répondre.