Управление памятью iPhone и выпуск - PullRequest
6 голосов
/ 05 мая 2009

Вот обычная практика, которую я часто вижу (в том числе из очень популярной книги для разработчиков iPhone)

В файле .h:

@interface SomeViewController : UIViewController
{
  UIImageView *imgView;
}

Где-то в файле .m:

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

И позже мы видим это ...

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

} 

Так как imgView имеет соответствующие alloc и release, необходим ли выпуск imgView в dealloc?

Где учитывается imgView, сохраняемый вызовом addSubview?

Ответы [ 6 ]

9 голосов
/ 05 мая 2009

Код неверный. В итоге вы освободите imgView после того, как он был освобожден.

В вашем .m файле вы:

  1. alloc это -> у вас есть это
  2. добавьте его в качестве подпредставления -> вы и UIView владеет им
  3. release это -> вам не принадлежит

Затем в dealloc вы release imgView, хотя, как мы установили на шаге 3 выше, он вам не принадлежит. Когда вы звоните [super dealloc], представление освободит все свои подпредставления, и я думаю, вы получите исключение.

Если вы хотите сохранить ивар в imgView, я советую , а не , вызывать release после добавления его в качестве подпредставления и оставить ваш dealloc таким же. Таким образом, даже если imgView в какой-то момент удален из иерархии представления, у вас все равно будет действительная ссылка на него.

0 голосов
/ 15 мая 2009

(у меня недостаточно репутации, чтобы добавить комментарий .)

@ bentford: поправьте меня, если я ошибаюсь, но я считаю, что для использования синтезированного установщика свойства imgView вы должны использовать «self.imgView»:

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

Если у вас нет self. , он просто использует ивар и не получает дополнительного удержания.

0 голосов
/ 14 мая 2009

Да, у этого кода есть проблемы. Он слишком рано выпускает imgView, что в редких случаях может привести к сбоям сохраняет объект в переменной экземпляра, не сохраняя его, и, как правило, неправильно управляет памятью.

Один правильный способ сделать это будет:

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

А в реализации;

@synthesize imgView;

Где-то в модуле:

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

И сделка остается прежней:

- (void) dealloc
{
    [imgView release];
    [super dealloc];
}
0 голосов
/ 05 мая 2009

Основной ответ таков: в примере кода должен быть только один [imgView release] ( независимо от того, идет ли он после addSubview или в dealloc ). Однако я бы удалил [imgView release] из dealloc и оставил бы его после addSubview.

Есть ловушка на iPhone; с didReceiveMemoryWarning вы можете получить объекты (, включая весь вид ), выпущенные из-под вас. Если у вас есть набор сохранения для всего приложения, и вы не уважаете память, вы можете обнаружить, что приложение просто уничтожается.

Хороший пример:
если вы думаете о вложенном наборе из 3 представлений, выберите Вид 1-> Вид 2-> Вид 3. Далее рассмотрим вызовы 'viewDidLoad' и 'viewDidUnload'. Если пользователь в настоящее время находится в «Представлении 3», возможно, что View1 выгружен, и это то, где это становится неприятным.
Если вы разместили объект внутри viewDidLoad и не освободили его после добавления его в подпредставление, то ваш объект не будет освобожден, когда view1 выгружен, но view1 все еще выгружен.
viewDidLoad будет запущен снова, и ваш код будет запущен снова, но теперь у вас есть два экземпляра вашего объекта вместо одного; один объект будет находиться в нигде с ранее выгруженным видом, а новый объект будет для текущего видимого вида. Промойте, вспените и повторите, и вы обнаружите, что ваше приложение падает из-за утечек памяти.

В этом примере, если данный блок кода является энергозависимым и имеет шанс на повторное выполнение ( из-за памяти или незагруженного представления ), я бы удалил [imgView release]; из dealloc и оставил это после addSubView.

Вот ссылка на основные концепции сохранения / выпуска: http://www.otierney.net/objective-c.html#retain

0 голосов
/ 05 мая 2009

Неверный выпуск в init.

Вы упомянули "обычную практику" и неназванную книгу. Я предлагаю взглянуть на канонические примеры от Apple: ViewTransitions - хороший пример для этого случая (и 2 вида для загрузки;)

http://developer.apple.com/iphone/library/samplecode/ViewTransitions/index.html

0 голосов
/ 05 мая 2009

Код неверен, вы не должны выпускать его в методе init, просто когда вызывается dealloc (если вы хотите сохранить его как ivar, вам не нужно, если вам не нужен указатель на него в другом месте, так как addSubview: сохранит представление для вас).

Я полагаю, что причина, по которой он на самом деле не падает, заключается в том, что он все еще сохраняется суперклассом (от вызова addSubview :), поэтому, когда он выпущен в dealloc, он фактически сбалансирован. Представление, вероятно, удаляет себя из суперпредставления, когда оно сразу же освобождается, поэтому, когда вызывается [super dealloc], оно не перевыпускается. Это моя догадка, в аренду.

...