Разработка для iPhone - предупреждение о симуляции памяти

Фон:

У меня есть приложение с панелью вкладок. Каждая вкладка содержит контроллер навигации, позволяющий пользователю переходить от одного представления к другому, показывая подробную информацию о данных (каждое представление обрабатывается контроллером представления, и каждый класс контроллера представления имеет didReceiveMemoryWarning метод). Списки заполняются путем извлечения данных из веб-служб.

Проблема:

Когда я использую опцию «Оборудование> Имитация памяти» в iPhone Simulator, метод didReceiveMemoryWarning вызывается для ВСЕХ моих контроллеров представления - даже для того, который просматривает пользователь. Я не хочу очищать какой-либо контент, который используется активным контроллером представления. Как я могу этого добиться?

Какой метод должен иметь реализацию для перезагрузки данных после того, как данные были выпущены из-за предупреждения памяти? (Я вижу, что классы контроллера представления, которые содержат метод viewDidLoad вызова табличного представления, когда пользователь возвращается к этому представлению, но если представление содержит (скажем, UIWebView), тогда viewDidLoad метод не вызывается. Почему это так?)

Отредактировано (пятница, 30 января 2009 г., 15:10)

(Примечание: я использую построитель интерфейсов для создания представлений, а метод loadView закомментирован.)

Итак, когда контроллер представления получает сообщение с предупреждением о памяти, выполняются следующие шаги:

  1. Вызывается следующий метод:

    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning]; 
    }
    
  2. В результате вызова [super didReceiveMemoryWarning] автоматически вызывается [self setView:nil]?

  3. Если какие-либо ресурсы должны быть очищены, тогда setView метод должен быть перезаписан, чтобы очистить локальные ресурсы.

  4. [self setView:nil] не вызывается, если представление в настоящее время активно (по умолчанию). Правильно? - Мне действительно любопытно, какой метод принимает это решение и как?

Не могли бы вы подтвердить. Кроме того, я получал сообщение об ошибке после этого подхода, но добавление myObject = nil после выпуска myObject в методе dealloc класса контроллера устранило проблему. Спасибо.


person Mustafa    schedule 29.01.2009    source источник


Ответы (5)


Это старый вопрос, но я не вижу правильного ответа, поэтому вот оно:

Когда получено предупреждение о памяти, -didReceiveMemoryWarning вызывается во ВСЕХ контроллерах представления, независимо от того, являются они "текущими" или нет. Контроллеры представления просто прослушивают трансляцию событий предупреждения памяти.

Если представление контроллера представления не используется во время предупреждения о памяти, контроллер выгрузит его, установив для свойства значение nil. Как узнать, используется ли представление? По свойству -superview представления. Если view.superview равен нулю, представление не является частью какого-либо дерева и может быть безопасно выгружено.

Как только это произойдет, вызывается -viewDidUnload контроллера. Это правильное место для разгрузки любых выходов и всего, что будет воссоздано в -viewDidLoad.


Так для чего же -didReceiveMemoryWarning? В вашем контроллере могут быть объекты, экземпляры которых не создаются до тех пор, пока к ним не будет осуществлен доступ. Например, у вас может быть контроллер, которому иногда требуется большой кусок данных из файла, но не всегда. Для него можно задать следующие свойства:

- (NSData*)bigChunkOfData {
  // Get data from our instance variable _data, read from disk if necessary
  if (_data == nil) {
    _data = [[NSData alloc] initWithContentsOfFile:@"/path/to/data"];
  }
  return _data;
}

В этот раз данные будут считаны с диска, а затем сохранены в переменной экземпляра. Поскольку переменная _data создается по запросу, для нас безопасно выгружать ее в ситуациях нехватки памяти: она просто будет создана снова в следующий раз, когда она нам понадобится.

- (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];

  [_data release];
  _data = nil;  // <-- Very important: don't leave strong references dangling.
}
person xaethos    schedule 05.05.2011

Я делаю уборку так:

-(void)setView:(UIView*)view
{
    [super setView:view];
    if(view == nil)
    {
       // Our view has been cleared, therefore we should clean up everything 
       // we are not currently using
....

setView:nil вызывается UIViewController в ответ на предупреждение памяти, если это представление в настоящее время не отображается - что в основном то, что вы хотите знать.

РЕДАКТИРОВАТЬ

В ответ на последующие действия:

  1. Правильный.
  2. Я этим занимаюсь, и у меня это работает.
  3. Правильный. Реализация didReceiveMemoryWarning в UIViewController - вот что делает это. Если вы не переопределите didReceiveMemoryWarning, то будет вызвана реализация базового класса в UIViewController - если вы переопределите ее, очевидно, вам следует вызвать:

    [super didReceiveMemoryWarning]
    
person Airsource Ltd    schedule 29.01.2009
comment
Даже если я не переопределяю метод didReceiveMemoryWarning, мое представление очищается. Это почему? - person Mustafa; 30.01.2009
comment
Отсутствие переопределения означает, что вы получаете поведение по умолчанию, реализованное UIViewController, которое заключается в очистке вашего представления. Если ваше переопределение просто вызывает суперреализацию, это то же самое, что и полное отсутствие переопределения. - person Airsource Ltd; 30.01.2009

Чтобы гарантировать, что мне не придется обрабатывать это для каждого отдельного контроллера представления, который я пишу ... Я только что создал шаблон Xcode ViewController, который дает рекомендации по тому, какие объекты выпускать и когда ..

дополнительные объяснения здесь http://iphone2020.wordpress.com/2010/05/30/efficient-memory-handling-in-uiviewcontroller-part-1/

Надеюсь, он окажется полезным.

person Bharath    schedule 29.01.2011

Что касается предупреждений об управлении просмотром и памяти:

UIKit не только позволяет переходить обратно из контроллера представления, но также позволяет переходить к другим контроллерам представления из существующих. В таком случае будет выделен новый UIViewController, который затем будет загружен в представление. Старый контроллер представления уйдет за пределы экрана и станет неактивным, но по-прежнему будет владеть многими объектами - некоторые в настраиваемых свойствах и переменных, а другие в свойстве / иерархии представления. То же самое и с новым контроллером видимого представления в отношении его объектов представления.

Из-за ограниченного объема памяти мобильных устройств владение двумя наборами объектов - одним в контроллере представления вне экрана, а другим - в контроллере представления на экране - может быть слишком много для обработки. Если UIKit сочтет это необходимым, он может освободить часть памяти контроллера просмотра вне экрана, которая в любом случае не отображается; UIKit знает, какой контроллер представления находится на экране, а какой вне экрана, поскольку, в конце концов, именно он управляет им (когда вы вызываете presentModalViewController:animated: или dismissModalViewControllerAnimated:). Итак, каждый раз, когда он чувствует давление, UIKit генерирует предупреждение памяти, которое выгружает и освобождает ваше внеэкранное представление из иерархии представлений, а затем вызывает ваш собственный метод viewDidUnload, чтобы вы сделали то же самое для ваших свойств и переменных. UIKit автоматически выпускает self.view, что позволяет нам вручную выпускать наши переменные и свойства в нашем коде viewDidUnload. Это делается для всех контроллеров просмотра вне экрана.

Когда системе не хватает памяти, она выдает didReceiveMemoryWarning. Виды за пределами экрана будут восстановлены и освобождены при появлении предупреждения о памяти, но ваше экранное представление не будет выпущено - оно видно и необходимо. Если вашему классу принадлежит большой объем памяти, такой как кеши, изображения и т.п., didReceiveMemoryWarning - это то место, где вы должны очистить их, даже если они отображаются на экране; в противном случае ваше приложение может быть остановлено из-за перегрузки системных ресурсов. Вам нужно переопределить этот метод, чтобы убедиться, что вы очистили свою память; просто помните, что вы звоните [super didReceiveMemoryWarning];.

Еще более подробное объяснение доступно здесь: http://myok12.wordpress.com/2010/11/30/custom-uiviewcontrollers-their-views-and-their-memory-management/

person Ohad Kravchick    schedule 01.12.2010

К счастью, в симуляторе есть удобная функция, которая позволяет тестировать ситуации с нехваткой памяти. Поместите несколько операторов NSLog () в viewDidLoad и didReceiveMemoryWarning, например: 

- (void)viewDidLoad {
    NSLog(@"viewDidLoad"); 
    ...
}

- (void)didReceiveMemoryWarning {
    NSLog(@"didReceiveMemoryWarning");
}
person Ricardo    schedule 29.05.2014