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

StackOverflow https://stackoverflow.com/questions/491075

Вопрос

Предыстория:

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

Проблема:

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

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

Отредактировано (пятница, 30 января 2009 - 03:10 вечера)

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

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

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

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

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

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

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

Это было полезно?

Решение

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

При получении предупреждения о памяти, -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.
}

Другие советы

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

-(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]
    

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

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

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

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

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

Из-за ограниченного объема памяти мобильных устройств владение двумя наборами объектов – одним в контроллере просмотра вне экрана и другим в контроллере просмотра на экране – может оказаться слишком сложным для обработки.Если 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/

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

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

- (void)didReceiveMemoryWarning {
    NSLog(@"didReceiveMemoryWarning");
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top