2009-05-05 21 views
6

Esto es una práctica común a menudo veo (incluyendo un libro de desarrollador de iPhone muy popular)iPhone Gestión de memoria y liberación

En el archivo .h:

@interface SomeViewController : UIViewController 
{ 
    UIImageView *imgView; 
} 

En algún lugar en el archivo .m:

imgView = [[UIImageView alloc] initWithFrame:[[UIScreen mainScreen] 
applicationFrame]]; 
[imgView setImage:[UIImage imageNamed:@"someimage.png"]]; 
[self addSubview:imgView]; 
[imgView release]; 

Y después, vemos esto ...

- (void) dealloc 
{ 
    [imgView release]; 
    [super dealloc]; 

} 

Dado que imgView tiene una asignación y una liberación correspondientes, ¿es necesario el lanzamiento de imgView en dealloc?

¿Dónde está retenida la imgView por la llamada addSubview?

Respuesta

-1

Sí, ese código tiene problemas. Es lanza el imgView demasiado pronto, lo que podría causar bloqueos en circunstancias excepcionales. almacena un objeto en una variable de instancia sin conservarlo, y generalmente se trata de la gestión de memoria de la manera incorrecta.

Una forma correcta de hacer esto sería:

@interface SomeViewController : UIViewController 
{ 
    UIImageView *imgView; 
} 
@property (nonatomic, retain) UIImageView *imgView; 

Y en la implementación;

@synthesize imgView; 

En algún lugar en el módulo:

//Create a new image view object and store it in a local variable (retain count 1) 
UIImageView *newImgView = [[UIImageView alloc] initWithFrame:self.view.bounds]; 
newImgView.image = [UIImage imageNamed:@"someimage.png"]; 

//Use our property to store our new image view as an instance variable, 
//if an old value of imgView exists, it will be released by generated method, 
//and our newImgView gets retained (retain count 2) 
self.imgView = newImgView; 

//Release local variable, since the new UIImageView is safely stored in the 
//imgView instance variable. (retain count 1) 
[newImgView release]; 

//Add the new imgView to main view, it's retain count will be incremented, 
//and the UIImageView will remain in memory until it is released by both the 
//main view and this controller. (retain count 2) 
[self.view addSubview:self.imgView]; 

Y el dealloc sigue siendo el mismo:

- (void) dealloc 
{ 
    [imgView release]; 
    [super dealloc]; 
} 
+3

imgView no se lanzó demasiado pronto. Se está reteniendo por addSubview. Esta es una muy idioma común para liberar justo después de agregar a través de addSubview (o cualquier otra llamada que retenga, como pushViewController de UINavigationController). – Boon

+1

Un método un poco más simple es asignar el ivar (imgView) directamente en lugar de usar self.imgView más tarde. Esto elimina la necesidad de [newImgView versión] más adelante en el código. – Sophtware

+0

@boon Vaya, tiene razón, creo que debo haber leído mal el código original. En cualquier caso, almacenar un elemento en una variable de instancia después de haberlo liberado es un error, podría terminar escribiendo un código que envíe un mensaje al objeto después de que haya sido desasignado (aunque admito que es muy poco probable en este caso específico). –

9

El código es incorrecto. Terminará lanzando imgView después de que haya sido desasignado.

En el archivo .m, que:

  1. alloc que -> lo propio
  2. agregarlo como una subvista -> usted y the UIView owns it
  3. release que -> que Don 't lo tienen

Luego, en dealloc, que release imgView a pesar de que, tal como se estableció en el paso 3 anterior, usted no lo posee. Cuando llame al [super dealloc], la vista lanzará todas sus subvistas, e imagino que obtendrá una excepción.

Si desea mantener una Ivar de imgView, sugiero no llamando release después de agregarlo como una subvista, y mantener su dealloc la misma. De esta forma, incluso si imgView se elimina en algún momento de la jerarquía de vistas, aún tendrá una referencia válida.

0

El código es incorrecto, no debe soltarlo en el método init, justo cuando se llama a dealloc (es decir, si desea mantenerlo como un ivar, no necesita hacerlo a menos que necesite un puntero a en otro lugar desde addSubview: conservará la vista para usted).

Creo que la razón por la que no está fallando en realidad es porque todavía está siendo retenida por la superclase (de la llamada a addSubview :), por lo que cuando se libera en dealloc eso está realmente compensado. La vista probablemente se elimine de la supervista cuando se desasigna inmediatamente después, por lo que cuando se llama a [super dealloc], no se está liberando en exceso. Esa es mi corazonada, en alquiler.

0

La respuesta básica es, sólo debe haber una [imgView release] en el código de ejemplo (ya sea después de addSubview o en dealloc). Sin embargo, eliminaría [imgView release] de dealloc y lo dejaría después de addSubview.

Hay una trampa en el iPhone; con didReceiveMemoryWarning, podría tener objetos (incluyendo una vista completa) liberados de usted. Si tiene un conjunto de retención de toda la aplicación y no respeta la memoria, entonces podría encontrar que la aplicación simplemente se eliminó.

Un buen ejemplo es:
si se piensa en un conjunto anidado de 3 puntos de vista, Ver 1- 2-> Ver> Ver 3. A continuación, consideremos el 'viewDidLoad' y 'viewDidUnload' llamadas. Si el usuario se encuentra actualmente en 'Vista 3', es posible que View1 esté descargado, y aquí es donde se pone desagradable.
Si asignó un objeto dentro de viewDidLoad y no lo lanzó después de agregarlo a la subvista, entonces su objeto no se libera cuando view1 está descargado, pero, view1 aún está descargado.
viewDidLoad se ejecutará de nuevo y su código se ejecutará de nuevo, pero ahora tiene dos instancias de su objeto en lugar de uno; un objeto estará en ningún lado con la vista previamente descargada y el nuevo objeto será para la vista actualmente visible. Enjuague, haga espuma y repita, y encontrará que su aplicación se bloquea debido a pérdidas de memoria.

En este ejemplo, si el bloque de código dada es volátil y tiene la oportunidad de volver a ser ejecutado (ya sea debido a la memoria o una vista sin carga), me retire de [imgView release]; dealloc y dejarlo después addSubview.

Aquí hay un enlace en los conceptos básicos retener/liberación: http://www.otierney.net/objective-c.html#retain

+0

Entonces me pregunto ¿por qué tener una aplicación denotar el.. imgView en el archivo .h en absoluto? Estoy preguntando seriamente porque no lo entiendo. Si lo liberas justo después de que hayas terminado de asignar la propiedad .image, ¿por qué no instanciar el imgView justo antes de usarlo? – Jann

+0

Estás correcto. Para OP, probablemente no sea necesario. ¿Se usa la imagen en algún otro lugar? Entonces probablemente sea necesario. – nessence

0

(no tengo la reputación suficiente para añadir comentario todavía.)

@bentford: Corrígeme si me equivoco, pero creo que en o Para utilizar el setter sintetizado de la propiedad imgView, debe usar "self".imgView ":

self.imgView = [[UIImageView alloc] initWithFrame:[[UIScreen mainScreen] 

Si usted no tiene auto, es sólo mediante el Ivar, y no está consiguiendo retener el adicional

+0

Creo que estaba confundido. Eliminé mi respuesta ya que no estaba ayudando. Gracias por la respuesta. – bentford