2009-12-20 4 views
2

Je suis un débutant dans Mac Dev. Je viens de l'iPhone dev. Ma question concerne la gestion des fenêtres non modales. C'est assez différent de l'iPhone et de son modèle de gestion de la mémoire.Gestion de la mémoire Windows non modale

Dites par exemple, j'ai une fenêtre de préférence, je peux utiliser quelque chose comme ça pour afficher la fenêtre:

-(IBAction)showPreferenceController:(id)sender { 
    if (!preferenceController) { 
    preferenceController = [[PreferenceController alloc]init]; 
    } 
    [preferenceController showWindow:preferenceController]; 
} 

Mais avec ce code, la fenêtre restera dans la mémoire pendant la vie de l'application, car la fenêtre est jamais libéré.

Pour éviter cela, je pourrais aussi utiliser la méthode décrite ici:
stackoverflow.com/questions/1391260/who-owns-an-nswindowcontroller-in-standard-practice
Créer dans PreferenceController un + (id) sharedInstance et libérer le fenêtre en utilisant (void)windowWillClose:(NSNotification *)notification

Je vois beaucoup d'échantillons de code de cacao où les fenêtres non-modales ne sont jamais libérées. Par exemple ici: http://www.mattballdesign.com/blog/2008/10/01/building-a-preferences-window/: Le panneau de préférences et toutes les sous-vues sont créés en awakeFromNib et seront donc en mémoire pendant toute la vie de l'application.

Si vous prenez par exemple l'application Xcode, il y a beaucoup de fenêtres non modales:
- fenêtre Recherche globale (CMD + MAJ + F)
- Panel App Info
- Aide Fenêtre
-.

Je suppose que ces fenêtres sont libérées lorsqu'elles sont fermées pour maintenir la mémoire aussi faible que possible. Je voudrais quelques conseils pour connaître la meilleure façon de gérer les fenêtres non-modales dans une application de cacao. Garder en mémoire? Libérer dès que possible? Je sais qu'un mac a beaucoup de mémoire par rapport à un iPhone mais je pense aussi qu'il n'est pas bon de garder des objets mémoire que nous n'utilisons pas.

Merci.

édité avec Rob après:

J'envoyer -autorelease à la fenêtre et mettez mon pointeur à zéro alors je recréerai la fenêtre plus tard. Ceci est similaire à la technique que vous citez, bien que l'utilisation de + sharedController pour le contrôleur ne soit pas liée; vous pouvez le faire si vous avez un contrôleur partagé ou non.

Je ne sais pas comment faire cela sans singleton (+ sharedController).
j'expliquer ce que je veux dire avec cet exemple:
Dans l'application Controller:

@interface AppController : NSObject <NSApplicationDelegate> { 

Mise en œuvre:

-(IBAction)showPreferenceController:(id)sender { 
    if (!preferenceController) { 
    preferenceController = [[PreferenceController alloc]init]; 
    } 
    [preferenceController showWindow:preferenceController]; 
} 

Dans le contrôleur de préférences:

@interface PreferenceController : NSWindowController <NSWindowDelegate> 

Mise en œuvre:

- (void)windowWillClose:(NSNotification *)notification { 
    [self autorelease];self=nil; 
} 

Il se bloque lorsque je ferme et rouvre après la fenêtre: preferenceController est libéré mais pas égal à zéro. J'ai donc besoin de mettre à zeroController quand la fenêtre est fermée. Il n'y a aucun problème à le faire avec un singleton.
Sans singleton, je devrais définir appController en tant que délégué de la fenêtre de préférences pour être en mesure de définir le paramètre PreferController à zéro lorsque la fenêtre est fermée. Mais je n'aime pas ça.

Edité avec Preston commente
Je ne dis pas, mais je ne veux qu'une seule instance de ma fenêtre non modale même si nous appelons -(IBAction)showPreferenceController:(id)sender plusieurs fois.
C'est pourquoi je teste si preferenceController est nul ou pas dans appController.
Donc, j'ai besoin de mettre à zéro dans le programme appController si vous fermez la fenêtre.
Donc, la solution serait:
En AppController, écoute NSWindowWillCloseNotification:

- (void)windowWillClose:(NSNotification *)notification { 
    if ([notification object] == [preferenceController window]) { 
     [preferenceController autorelease]; 
     preferenceController = nil; 
    } 
} 

est-il exact? Est-ce la seule solution? parce que cela semble un peu compliqué de gérer ma fenêtre non modale ...

+1

Vous ne devriez pas vous libérer ici (vous ne devriez presque jamais appeler [self release]). Vous devriez libérer self.window et régler self.window = nil. –

+1

Aussi, je recommanderais ceci étant [self.window autorelease]; self.window = nil; ' De cette façon, la fenêtre peut finir de se fermer avant d'être libérée. –

+0

Merci Rob, mon exemple était juste pour expliquer le problème car comme dit, l'exemple va planter. Je veux mettre PreferController à zéro et cela doit être implémenté dans appController. J'ai édité ma question avec vos derniers commentaires. – Benoit

Répondre

3

Vous pensez de toutes les manières ici. Ce n'est pas correct de perdre de la mémoire juste parce que vous en avez beaucoup. Cela dit, il est courant de ne pas publier des fenêtres juste parce qu'elles se ferment sur Mac. Vous devriez généralement les garder si vous pensez qu'ils vont être ouverts à nouveau pour éviter le coût du rechargement.

Il existe deux écoles de pensée sur NSWindow: détenue et sans propriétaire. Je suis de l'école de pensée «possédée». Je donne généralement à chaque fenêtre un propriétaire qui le conserve avec un ivar et le relâche le cas échéant. Généralement c'est le délégué, parfois c'est le contrôleur d'application. Dans -windowShouldClose:, j'envoie -autorelease à la fenêtre et définissez mon pointeur sur zéro, donc je vais recréer la fenêtre plus tard. Ceci est similaire à la technique que vous citez, bien que l'utilisation ou non de +sharedController pour le contrôleur ne soit pas liée; vous pouvez le faire si vous avez un contrôleur partagé ou non.

L'idée la plus répandue est d'utiliser le -setReleasedWhenClosed: de NSWindow pour qu'il se libère automatiquement lorsqu'il se ferme. Je crois que plusieurs des fenêtres que vous référez le font de cette façon lorsqu'elles sont fournies par le système, puisqu'il n'y a peut-être pas de délégué. Cela peut être utile dans certains types de panneaux, mais je serais prudent avec cela comme un modèle général. Il est préférable d'avoir une gestion de la mémoire explicite dans presque tous les cas.

+0

Merci Rob, je ferais mieux de comprendre la gestion des fenêtres. J'ai édité ma question suite à votre post parce que je ne comprends pas comment libérer la fenêtre sans objet singleton. – Benoit

1

Si vous cochez "Release When Closed" pour votre fenêtre dans Interface Builder ou si vous le définissez par programme, il se libère automatiquement.Vous pouvez également avoir le délégué de la fenêtre autorelease la fenêtre dans la méthode windowShouldClose: delegate.

Si vous souhaitez libérer le contrôleur de fenêtre lorsque la fenêtre se ferme, demandez à votre contrôleur de fenêtre d'écouter la notification NSWindowWillCloseNotification de sa fenêtre et de procéder à une auto-libération dans cette méthode. Je ne suis pas un fan de la création d'un contrôleur de préférences de singleton qui reste toujours en place avec lequel l'utilisateur interagira rarement avec lui.

+1

Merci. Dans l'aide de Xcode, pour setReleasedWhenClosed, "Release when closed" est ignoré pour les fenêtres appartenant aux contrôleurs de fenêtres. ". C'est mon cas. Dans ma question, j'ai mis un exemple avec autorelease dans "windowWillClose". Mais le problème est le suivant: je ne veux qu'une seule fenêtre même si nous appelons la fenêtre plusieurs fois. C'est pourquoi je teste si preferencesController est nul ou pas dans la méthode "showPreferenceController". Donc, j'ai besoin de mettre à zeroController quand la fenêtre est fermée. Je dois ensuite écouter NSWindowWillCloseNotification dans appController et non dans preferencesController. Vrai ou pas? – Benoit

+0

J'ai édité ma question avec votre commentaire afin de trouver la meilleure solution. – Benoit

+0

Ah, vous avez raison à propos de ce comportement de la libération de la fenêtre. Je faisais référence à un objet singleton global, pas à une variable d'instance habituelle. Une fois attribué, un objet singleton n'est pas libéré avant la fin de l'application. Oui, vous pouvez demander à l'objet propriétaire de votre contrôleur de préférences d'écouter la notification de fermeture de la fenêtre et de relancer automatiquement le contrôleur de préférences, puis de définir la variable d'instance sur zéro. Le contrôleur de préférences va libérer la fenêtre qu'il possède. Si l'utilisation de cette chose dans la mémoire vous préoccupe beaucoup, c'est comme ça que je le ferais. –