Разработка iPhone - Имитация памяти Предупреждение - PullRequest
5 голосов
/ 29 января 2009

Фон

У меня есть приложение панели вкладок. Каждая вкладка содержит контроллер навигации, позволяющий пользователю переходить из одного представления в другое, отображая детализированную информацию данных (каждое представление обрабатывается контроллером представления, и каждый класс контроллера представления имеет метод 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 метод класса контроллера решило проблему. Спасибо.

Ответы [ 5 ]

12 голосов
/ 05 мая 2011

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

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

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

Как только это происходит, вызывается -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.
}
8 голосов
/ 29 января 2009

Я убираюсь так:

-(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 в ответ на предупреждение памяти, если это представление в данный момент не отображается - это то, что вы хотите знать.

EDITED

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

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

    [super didReceiveMemoryWarning]
    
1 голос
/ 29 января 2011

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

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

Надеюсь, это будет полезно.

1 голос
/ 01 декабря 2010

В отношении управления представлением и предупреждениями памяти:

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/

0 голосов
/ 29 мая 2014

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

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

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