Gerenciamento de memória iPhone e liberação
-
03-07-2019 - |
Pergunta
Aqui é uma prática comum que vejo frequentemente (incluindo de um livro muito popular desenvolvedor iPhone)
No arquivo .h:
@interface SomeViewController : UIViewController
{
UIImageView *imgView;
}
Em algum lugar no arquivo .m:
imgView = [[UIImageView alloc] initWithFrame:[[UIScreen mainScreen]
applicationFrame]];
[imgView setImage:[UIImage imageNamed:@"someimage.png"]];
[self addSubview:imgView];
[imgView release];
E depois, vemos isso ...
- (void) dealloc
{
[imgView release];
[super dealloc];
}
Desde imgView tem uma alocação de correspondência e de liberação, é a liberação de imgView em dealloc necessário?
Onde está a imgView retidos pela chamada addSubview contabilizada?
Solução
Sim, esse código tem problemas. É libera o imgView muito cedo que poderia potencialmente causar acidentes em circunstâncias raras lojas de um objeto em uma variável de instância sem mantê-la, e é geralmente só vai sobre gerenciamento de memória de forma errada.
Uma maneira correta de fazer isso seria:
@interface SomeViewController : UIViewController
{
UIImageView *imgView;
}
@property (nonatomic, retain) UIImageView *imgView;
E na implementação;
@synthesize imgView;
Em algum lugar no 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];
E o dealloc permanece o mesmo:
- (void) dealloc
{
[imgView release];
[super dealloc];
}
Outras dicas
O código é incorreta. Você vai acabar liberando imgView
depois de ter sido desalocada.
Em seu arquivo .m, você:
-
alloc
-lo -> você possui it - adicioná-lo como um subview -> você e o UIView é o dono
-
release
-lo -> você não possui-lo
Então, em dealloc
, você release
imgView embora, como estabelecido no passo 3 acima, você não ele próprio. Quando você chama [super dealloc]
, a visão vai lançar todos os seus subviews, e eu imagino que você vai receber uma exceção.
Se você quiser manter um ivar de imgView
, sugiro não chamando release
depois de adicioná-lo como um subview, e manter o seu dealloc
o mesmo. Dessa forma, mesmo se imgView
é em algum ponto removido da hierarquia de exibição, você ainda tem uma referência válida para ele.
O código é incorreto, você não deveria estar lançando-o no método init, justamente quando dealloc é chamado (Isto é, se você quiser mantê-lo como um ivar, você não precisa a menos que você precisa de um ponteiro para ele em outros lugares desde addSubview:. reterá a vista para você)
Acredito que a razão não é realmente falhando é porque ainda está sendo retida pela superclasse (a partir da chamada para addSubview :), então quando for lançado em dealloc que está realmente equilibrado. O ponto de vista, provavelmente, remove-se do superview quando é desalocada imediatamente depois, por isso, quando [super dealloc]
é chamado não está sendo over-lançado. Esse é o meu palpite, pelo arrendamento.
Release em init está incorreto.
Você mencionou "prática comum" e um livro chamado-un. Sugiro olhar para os exemplos canônicos da Apple: ViewTransitions é um bom exemplo para este caso (e 2 vistas para inicialização;)
http://developer.apple.com/iphone/library /samplecode/ViewTransitions/index.html
(Eu não tenho reputação suficiente para comentário add ainda.)
@bentford: me corrija se eu estiver errado, mas acredito que, a fim de usar setter sintetizado da propriedade imgView, você deve usar "self.imgView":
self.imgView = [[UIImageView alloc] initWithFrame:[[UIScreen mainScreen]
Se você não tem self. , é só usar o ivar, e ele não está recebendo reter o adicional.
A resposta básica é, deve haver apenas um [imgView release]
no código de exemplo ( se é depois addSubview ou em dealloc ). No entanto, gostaria de remover [imgView release]
de dealloc
e deixá-lo após addSubview
.
Há um prendedor no iPhone; com didReceiveMemoryWarning
, você poderia ter objetos (, incluindo uma vista inteira ) lançado debaixo de você. Se você tiver uma aplicação em toda a reter set e você não respeitar a memória, então você pode encontrar o aplicativo simplesmente ser morto.
Um exemplo bom é:
se você pensar em um conjunto aninhado de 3 vistas, Ver 1-> Ver 2> View 3.
Em seguida, considere o 'viewDidLoad
' e chamadas dos viewDidUnload
'. Se o usuário está atualmente em 'View 3', é possível que View1 é descarregado, e isso é onde fica desagradável.
Se você alocado um objeto dentro viewDidLoad
e não liberá-lo depois de o adicionar à subexibição, então o seu objeto não é liberado quando view1 é descarregado, mas, view1 ainda é descarregado.
viewDidLoad
será executado novamente e seu código será executado novamente, mas agora você tem duas instâncias de seu objeto em vez de um; um objeto estará em nowhereland com a visão previamente descarregados e o novo objeto será para a vista visível no momento. Rinse, espuma e repita e você encontrar a sua aplicação deixar de funcionar a partir de vazamentos de memória.
Neste exemplo, se o bloco de dados de código é volátil e tem uma chance de ser executado novamente ( se por causa de memória ou uma visão descarregada ), eu iria remover [imgView release];
de dealloc e deixá-lo após addSubview.
Aqui está um link em reter conceitos básicos / Lançamento: http://www.otierney.net/objective-c.html#retain