2009-05-05 21 views
6

Ecco una pratica comune che vedo spesso (anche da un libro iPhone sviluppatore molto popolare)iPhone gestione della memoria e rilascio

Nel file .h:

@interface SomeViewController : UIViewController 
{ 
    UIImageView *imgView; 
} 

Da qualche parte nel file .m:

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

E più tardi, vediamo questo ...

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

} 

Poiché imgView ha un allocazione e una release corrispondenti, è necessario il rilascio di imgView in dealloc?

Dove è rappresentato l'imgView trattenuto dalla chiamata ad addSubview?

risposta

-1

Sì, quel codice ha problemi. Lo rilascia troppo presto l'imgView, il che potrebbe potenzialmente causare arresti anomali in circostanze rare. memorizza un oggetto in una variabile di istanza senza conservarlo, e generalmente si occupa solo della gestione della memoria nel modo sbagliato.

Un modo corretto di fare questo sarebbe:

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

E nell'attuazione;

@synthesize imgView; 

Da qualche parte nel modulo:

//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]; 

E il dealloc rimane lo stesso:

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

imgView non è stato rilasciato troppo presto, è stato mantenuto da addSubview. idioma comune da rilasciare subito dopo l'aggiunta tramite addSubview (o qualsiasi altra chiamata che manterrà, come pushViewController di UINavigationController. – Boon

+1

Un metodo leggermente più semplice consiste nell'assegnare direttamente ivar (imgView) invece di utilizzare self.imgView in seguito. Ciò elimina la necessità di [versione newImgView] più avanti nel codice. – Sophtware

+0

@boon Oops, hai ragione - penso di aver letto male il codice originale. Ad ogni modo, memorizzare qualcosa in una variabile di istanza dopo averlo rilasciato è un errore, potresti finire per scrivere codice che invia un messaggio all'oggetto dopo che è stato deallocato (anche se ammetto che è molto improbabile in questo caso specifico). –

9

Il codice non è corretto. Finirai per rilasciare imgView dopo che è stato deallocato.

Nel file .m, è:

  1. alloc esso -> si è posseduta
  2. inserirlo come una visualizzazione secondaria -> voi e the UIView owns it
  3. release esso -> don 't possiedono lo

Poi, nel dealloc, è release imgView anche se, come abbiamo stabilito al punto 3 di cui sopra, non si possiede. Quando chiami [super dealloc], la vista rilascerà tutte le sue sottoview e immagino che otterrai un'eccezione.

Se si desidera mantenere un Ivar di imgView, suggerisco non chiamando release dopo aver aggiunto come una visualizzazione secondaria, e mantenere il vostro dealloc lo stesso. In questo modo, anche se imgView viene rimosso dalla gerarchia della vista, avrai comunque un riferimento valido.

0

Il codice non è corretto, non dovresti rilasciarlo nel metodo init, solo quando viene chiamato dealloc (che è se vuoi mantenerlo come un ivar, non è necessario a meno che non sia necessario un puntatore a altrove visto che addSubview: manterrà la vista per te).

Credo che il motivo per cui in realtà non si blocca è perché è ancora trattenuto dalla superclasse (dalla chiamata ad addSubview :), quindi quando è rilasciato in dealloc è in realtà bilanciato. La vista probabilmente rimuove se stessa dalla superview quando viene rilasciata subito dopo, quindi quando viene chiamato [super dealloc] non viene sovrascritto. Questa è la mia impressione, in affitto.

0

La risposta di base è, ci dovrebbe essere un solo [imgView release] nel codice di esempio (se è dopo addSubview o in dealloc). Tuttavia, rimuovere [imgView release] da dealloc e lasciarlo dopo addSubview.

C'è una presa su iPhone; con didReceiveMemoryWarning, è possibile avere oggetti (compresa un'intera vista) rilasciati da sotto di voi. Se disponi di un set di conservazione a livello di applicazione e non rispetti la memoria, potresti trovare l'applicazione semplicemente uccisa.

Un buon esempio è:
se si pensa di un insieme nidificato di 3, Vista sulla 1-> Visualizza> Visualizza 2- 3. Avanti, considerano il 'viewDidLoad' e 'viewDidUnload' chiamate. Se l'utente è attualmente in 'Visualizza 3', è possibile che View1 sia scaricato, e questo è dove diventa cattivo.
Se si assegnava un oggetto all'interno di viewDidLoad e non lo si rilasciava dopo averlo aggiunto alla sottoview, l'oggetto non viene rilasciato quando viene scaricato view1, ma view1 è ancora scaricata.
viewDidLoad verrà eseguito nuovamente e il codice verrà eseguito di nuovo, ma ora hai due istanze del tuo oggetto anziché uno; un oggetto sarà in nowhereland con la vista precedentemente scaricata e il nuovo oggetto sarà per la vista attualmente visibile. Risciacquare, schiuma e ripetere e si trova l'applicazione si arresta in modo anomalo da perdite di memoria.

In questo esempio, se il blocco specificato di codice è volatile e ha la possibilità di eseguire nuovamente (sia a causa di memoria o una vista scaricata), Rimuoverei [imgView release]; da dealloc e lasciare dopo addSubView.

Ecco un link sui concetti di base mantenere/release: http://www.otierney.net/objective-c.html#retain

+0

Poi mi chiedo perché si dovrebbe avere un'implementazione indicare il.. imgView nel file .h? Mi chiedo seriamente di non averlo. Se rilasci subito dopo aver assegnato la proprietà .image, perché non istanziare l'imgView giusto prima di usarlo? – Jann

+0

corretto Per OP, probabilmente non è necessario L'immagine è usata altrove? Probabilmente è necessario – nessence

0

(non ho abbastanza reputazione da add commento ancora.)

@bentford: correggimi se sbaglio, ma credo che dentro o per usare il setter sintetizzato della proprietà imgView, devi usare "self".imgView ":

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

Se non si dispone di , è solo con l'Ivar, e non sta facendo le ulteriori conservano

+0

Penso di essere stato confuso. Ho cancellato la mia risposta in quanto non stava aiutando. Grazie per il feedback. – bentford

Problemi correlati