2009-09-07 14 views
12

Estoy buscando más aclaración después de ver What is responsible for releasing NSWindowController objects?¿Quién posee un NSWindowController, en la práctica estándar?

Estoy escribiendo una sencilla aplicación de gestión de inventario para mis sobrinos. Tengo una vista de tabla que muestra el contenido de su "biblioteca", etc. Para agregar un nuevo elemento a la biblioteca, hacen clic en un botón "+". Este botón abre una nueva ventana que les solicita los detalles del artículo y valida la entrada cuando hacen clic en 'Aceptar'.

Todo eso está funcionando bien. Sin embargo, tengo una pregunta sobre la gestión de la memoria. Para crear la nueva ventana, yo uso el siguiente código:

- (IBAction)addNewItem:(id)sender { 
    LibraryItemEditorController *editorController = 
    [[LibraryItemEditorController alloc] 
      initWithWindowNibName:@"LibraryItemEditor"]; 

    [editorController showWindow:nil]; 
    // editorController is "leaked" here, it seems. 
} 

no puede liberar (ni autorelease) editorController al final de addNewItem:, porque nada más se está refiriendo editorController; si lo libero, la ventana desaparece inmediatamente. Sin embargo, quiero que se libere el controlador de la ventana una vez que se cierre su ventana. En leí de Window Programming Guide de Apple lo siguiente:

Si desea que el cierre de una ventana a hacer tanto controlador de ventana y ventana desaparece cuando no es parte de un documento , la subclase de NSWindowController puede observar NSWindowWillCloseNotification o, como el delegado ventana, poner en práctica el método windowWillClose: e incluyen la siguiente línea de código en su aplicación :

[self autorelease]; 

He usado [self autorelease] en el windowWillClose: método del controlador de ventana. Esto funciona, y no pierde memoria. Sin embargo, solo se siente feo; addNewItem: parece que está perdiendo memoria, y el análisis estático también lo cree. I sepa que en realidad se trata en windowDidClose:, pero simplemente se siente mal. Además, el controlador de ventana ahora se está liberando sin haberse retenido. Todo esto va en contra de las reglas de administración de memoria que he aprendido.

Mi otra opción es poner una Ivar en el controlador principal (ya sea un NSWindowController o un NSMutableSet de NSWindowController s) y luego ver la NSWindowWillCloseNotification en el controlador de matriz y lo liberan en respuesta. Esto es más limpio, y es probablemente lo que haré. También es mucho más trabajo, lo que me lleva a mis preguntas.

¿Está mirando para el NSWindowDidCloseNotification la manera estándar de hacer esto? ¿Cuál es la forma estándar de administrar NSWindowControllers que se crean y destruyen a pedido? ¿La opción [self autorelease] es la tradicionalmente recomendada, y es solo ahora que tenemos un análisis estático que esto es un problema?

Respuesta

5

Parece que su ventana es modal, en cuyo caso:

[NSApp runModalForWindow:[editorController window]]; 
[editorController release]; 

Aquí hay un patrón para las ventanas no modales:

@implementation QLPrefWindowController 

+ (id) sharedInstance 
{ 
    if (!_sharedInstance) 
    { 
     _sharedInstance = [[QLPrefWindowController alloc] init]; 
    } 
    return _sharedInstance; 
} 

- (void)windowWillClose:(NSNotification *)notification 
{ 
    if ([notification object] == [self window] && self == _sharedInstance) 
    { 
     _sharedInstance = nil; 
     [self release]; 
    } 
} 

Entonces cualquier persona que quiera acceder o visualizar la ventana puede hágalo a través del método de clase +sharedInstance.Si la ventana no está ya visible, se crea; de lo contrario, obtienen la ventana actualmente visible.

+0

No lo estaba ejecutando como una ventana modal, pero creo que probablemente debería. la versión sharedInstance tiene sentido para un panel de inspectores, creo. Y si realmente necesitaba tener varias ventanas abiertas al mismo tiempo, probablemente tendría sentido que las guardara directamente. Esto funciona para mí –

+0

No creo que esta respuesta sea correcta, ya que los métodos de clase no pueden acceder a las variables de instancia. En este caso, el método de clase "sharedInstance" está intentando acceder a un iVar llamado "_sharedInstance". Esto fallará todo el tiempo. – Bryan

+0

'_sharedInstance' sería una variable estática, no un ivar. Por lo general, se implementan las instancias compartidas. –

0

La solución para las situaciones no modales publicadas anteriormente no es correcta porque los métodos de clase no pueden acceder a iVars. He resuelto este problema mediante la creación de un método de clase (en mi subclase NSWindowController llamada LPWindowController) que tiene este aspecto:

+ (id) autoreleasingInstanceShowingWindow 
{ 
    LPWindowController *obj = [[LPWindowController alloc] initWithWindowNibName:@"myWindow"]; 
    [obj showWindow:[NSApp delegate]]; 

    return obj; 
} 

El método anterior devuelve una instancia de LPWindowController con una cuenta de retención de 1. También muestra la ventana del controlador . Esto es importante, porque de lo contrario tendríamos que confiar en que el llamante llame a "showWindow:" para que aparezca la ventana de LPWindowController. Si la persona que llama alguna vez no pudo hacer esto (lo que probablemente sería un error), el controlador nunca se lanzará. Al forzar que la ventana se muestre cuando asignamos/iniciamos el controlador, evitamos esta trampa.

A continuación, en IB que establece el delegado de nuestra ventana a la clase LPWindowController y poner esto en esa clase:

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

Y, por último, cuando necesitamos para crear y mostrar nuestra ventana, simplemente usamos el método siguiente en lugar de llamar a "alloc/initWithWindowNibName:" en LPWindowController.

LPWindowController *cont = [LPWindowController autoreleasingInstanceShowingWindow]; 
cont = nil; 

La segunda línea es importante. Primero, elimina una advertencia sobre "variable no utilizada cont". En segundo lugar, elimina el peligro de un puntero colgando. Una vez que la instancia de LPWindowController se libera, si no se cancela cont, apuntará a la memoria basura.

De todos modos, ese es mi enfoque recomendado para este problema.

Cuestiones relacionadas