¿Cuándo debería liberar objetos en - (void) viewDidUnload en lugar de en -dealloc?

Respuestas:

51

Además de lo que ya se ha indicado, quería elaborar más sobre la lógica detrás -viewDidUnload.

Una de las razones más importantes para implementarlo es que las UIViewControllersubclases comúnmente también contienen referencias de propiedad a varias subvistas en la jerarquía de vistas. Estas propiedades podrían haberse establecido IBOutletsal cargar desde una plumilla, o mediante programación dentro -loadView, por ejemplo.

La propiedad adicional de las subvistas UIViewControllersignifica que incluso cuando su vista se elimina de la jerarquía de vistas y se libera para ahorrar memoria, a través de la cual las subvistas también son liberadas por la vista, en realidad no se desasignarán porque la UIViewControllerpropia vista todavía contiene sus propios pendientes. conservando referencias a esos objetos también. Liberar la UIViewControllerpropiedad adicional de estos objetos asegura que también se desasignarán para liberar memoria.

Los objetos que libera aquí generalmente se recrean y configuran nuevamente cuando la UIViewControllervista es re-loaded, ya sea desde un Nib o mediante una implementación de -loadView.

También tenga en cuenta que la UIViewController viewpropiedad es nilen el momento en que se llama a este método.

Sean Patrick Murphy
fuente
1
Debe leer developer.apple.com/library/ios/#featuredarticles/… para comprender el ciclo de vida del controlador de vista / vista
Paul Solt
21

Como dice la documentación :

Se llama durante condiciones de poca memoria cuando el controlador de vista necesita liberar su vista y cualquier objeto asociado con esa vista para liberar memoria.

En la misma situación deallocestá no llama. Este método solo está disponible en OS3 y versiones posteriores. ¡Tratar con la misma situación en iPhone OS 2.x fue un verdadero dolor!

Actualización de julio de 2015 : debe tenerse en cuenta que viewDidUnloadquedó obsoleto en iOS 6 porque "Las vistas ya no se purgan en condiciones de poca memoria, por lo que este método nunca se llama". Entonces, el consejo moderno es no preocuparse por él y usarlo dealloc.

Stephen Darlington
fuente
6
También de los documentos: "Debes hacer esto solo para objetos que puedas volver a crear fácilmente más adelante, ya sea en tu método viewDidLoad o desde otras partes de tu aplicación. No debes usar este método para liberar datos de usuario o cualquier otra información que no pueda ser recreado fácilmente ". Esa es una pregunta que yo también tenía, ¡gracias!
leolobato
¿Qué pasa si la vista está visible actualmente? ¿No sería malo eliminarlo debido a una advertencia de poca memoria? ;) entonces la aplicación estaría vacía. No entiendo el punto de liberar la vista debido a la poca memoria. Si no veo una vista, siempre suelto todo el controlador. Aunque tengo un controlador de vista raíz que permanece intacto y gestiona toda la carga / descarga de sus controladores de vista infantil ...
Gracias
No, no usaría esto si simplemente cambiara una vista por otra. Piense en el caso en el que tiene una "pila" de vistas con un UINavigationController. Solo una vista es visible y, si tiene una advertencia de memoria, puede liberar todas las que no están visibles.
Stephen Darlington
¿Cómo se controla que no se llame a viewDidUnload en la vista visible actual como ha señalado Thanks?
arielcamus
1
viewDidUnload no se llamará en la vista actualmente visible, solo en las vistas que no son visibles.
Progrmr
9

Esto se debe a que normalmente configurará el @propertycomo "(nonatomic, retain)"y, como tal, el establecedor que se crea para usted libera el objeto actual y luego retiene el argumento, es decir

self.property = nil;

... hace algo como:

[property release];
property = [nil retain];

Por lo tanto, está matando dos pájaros de un tiro: administración de memoria (liberando el objeto existente) y asignando el puntero a nil (ya que enviar cualquier mensaje a un puntero nil devolverá nil).

Espero que ayude.

gazzaaa87
fuente
8

Recuerde que viewDidUnloades un método en el controlador de vista, no en la vista. La de vista dealloc método será llamado cuando la vista se descargue, pero la del controlador de vista dealloc método no puede ser llamado hasta más tarde.

Si recibe una advertencia de memoria baja y su vista no se muestra, lo que sucederá, por ejemplo, cada vez que use un UIImagePickerController para permitir que el usuario tome una foto, su vista se descargará y deberá volver a cargarla después de eso.

David Maymudes
fuente
eso tiene sentido. ¿Qué sucede si siempre dejo caer todo el controlador de vista? Eso es lo que realmente hago. En este caso, no tengo que lidiar mucho con -viewDidUnload, ¿verdad? Nunca he tenido la situación en la que dejaría solo la vista, ya que siempre dejo caer todo el controlador si no está visible de todos modos.
Gracias
bueno, solo recuerde que en un caso en el que se muestre su vista, pero tenga una vista de pantalla completa como ImagePicker encima, su vista podría descargarse incluso si no lo planeó.
David Maymudes
6

Conclusión:

Los controladores de vista tienen una propiedad de vista. Normalmente, una plumilla o un fragmento de código agrega otras vistas a esta vista. Esto sucede a menudo dentro de un método -viewDidLoad, como este:

- (void)viewDidLoad {
    [super viewDidLoad];
    [self createManyViewsAndAddThemToSelfDotView];
}

Además, un archivo nib puede crear un botón y agregarlo a la vista del controlador de vista.

En iPhone OS 2.2, cuando se invocaba -didReceiveMemoryWarning desde el sistema, tenía que liberar algo para liberar memoria. Podría liberar la vista del controlador de vista completo si eso tuviera sentido. O simplemente contenidos que consumen mucha memoria.

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that's not essential, such as cached data
}

Ahora, en el nuevo OS 3.0, hay un método -viewDidUnload, que se invocará desde el sistema cuando la vista se haya descargado debido a poca memoria (corríjame: ¿cuándo exactamente se llama a esto?)

-viewDidUnload se usa para liberar todos los objetos que eran propiedad tanto del controlador de vista como de la vista. La razón: si un controlador de vista contiene referencias a elementos secundarios de la vista, es decir, un botón, las vistas secundarias a las que se hace referencia no se liberarán, porque su recuento de retención es> = 1. Una vez que se publican en -viewDidUnload, pueden liberarse de memoria.

Gracias
fuente
1
el recordatorio a la vista se descargó para hacer self.button = nil ;, not [button release] ;.
mk12
6

Apple desaprobó viewWillUnload, ahora debe usar didReceiveMemoryWarning o dealloc para liberar sus objetcs.

En iOS 6, los métodos viewWillUnload y viewDidUnload de UIViewController ahora están en desuso. Si estaba utilizando estos métodos para liberar datos, utilice el método didReceiveMemoryWarning en su lugar. También puede utilizar este método para liberar referencias a la vista del controlador de vista si no se está utilizando. Debería probar que la vista no está en una ventana antes de hacer esto.

Guilherme Torres Castro
fuente
5

Si el controlador de vista se extrae de la pila de controladores de navegación y no se conserva en ningún otro lugar, se desasignará y se llamará a dealloc en lugar de viewDidUnload. Debería liberar las vistas creadas en loadView en dealloc, pero no es necesario establecer las variables en nil, porque poco después de llamar a dealloc, las variables dejarán de existir.

Colin
fuente
3

Puede liberar cualquier subvista que conserve, por ejemplo, ese UIImageView que retuvo en su método loadView, o mejor aún, la imagen que estaba en ese UIImageView.

drvdijk
fuente