2012-03-06 2 views
0

Я использую «знаменитый» шаблонный код, который почти каждый использует для обработки cellForRowAtIndexPath:является dequeueReusableCellWithIdentifier обязательным для предотвращения утечек памяти?

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"someCustomCellID"]; 
if (cell == nil) // nothing to recycle from the queue: create a new cell 

, но это даст мне много причин проблем»мои клетки содержат изображения, которые я загружаю асинхронный и две функциональные (освобождение пакета из очереди и асинхра load) часто конфликтуют. Поэтому я стараюсь создавать каждый раз новую ячейку, и она работает очень хорошо и быстро. Но у меня есть сомнения: должен ли я по-прежнему вызывать dequeueReusableCellWithIdentifier для освобождения памяти, даже если я игнорирую возвращаемое значение и каждый раз создаю ячейки? Я предполагаю, что клетки больше не используются автоматически освобождаться (как должно быть), но мне интересно, если очередь кэширования может потребовать явную «бесплатно» с вызовом DEQUEUE ...

ответ

4

dequeueReusableCellWithIdentifier не предназначен для предотвращения памяти утечки, но он предназначен для повышения производительности (одним из способов является сокращение использования памяти). При использовании метода dequeue прокрутка табличного представления будет намного более гладкой, если имеется несколько строк данных. Я бы порекомендовал вам загрузить вашу асинхронную загрузку с помощью метода dequeue, особенно если вы заметили какое-либо отставание во время прокрутки. Если вам нужен пример того, как это сделать, см. Apple's LaxyTableImages Example. Однако, если вы это сделаете, убедитесь, что вы не хотите повторно использовать ячейки, а затем просто передайте nil как reuseIdentifier при создании ваших ячеек.

+0

Фактически не повторное использование ячеек является более плавным! Я стараюсь с 1K ячейками. Проблема с асинхронной загрузкой заключается в том, что вы можете закончить загрузку изображения в неправильную ячейку, потому что она повторно используется до того, как фоновый процесс завершит обработку изображения. Чтобы прояснить мой комментарий: я не говорю, что dequeueing является неправильным (конечно, нет), просто это не соответствует моему конкретному делу. –

+0

Как правило, если вы правильно деактивируете ячейки, у вас будет выигрыш в производительности. Посмотрите пример, предоставленный Apple, я добавил к ответу. – Joe

+0

Могу ли я ошибаться (вроде бы здесь поздно), или у этого примера есть большая проблема лениво загружать ВСЕ изображение в память? Если я прав, это решение потенциально может иметь огромное влияние на память ... –

0

Да, вам нужно использовать dequeueReusableCellWithIdentifier, чтобы избежать утечек, если вы динамически выделяете ячейки.

Предположительно ваш асинхронная загрузка изображений работает примерно так:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"]; 
    if (!cell) { 
     ... 
    } 

    NSURLRequest *request = [self URLRequestForIndexPath:indexPath]; 
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] 
     completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { 
      cell.imageView.image = [UIImage imageWithData:data]; 
     }]; 

    return cell; 
} 

Этот код имеет проблемы. К моменту завершения обработчика завершения представление таблицы может повторно использовать ячейку для другой строки!

Нам нужно изменить блок завершения, так что смотрит в камеру для строки, когда она готова для установки изображения:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"]; 
    if (!cell) { 
     ... 
    } 

    NSURLRequest *request = [self URLRequestForIndexPath:indexPath]; 
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] 
     completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { 
      UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; 
      if (cell) 
       cell.imageView.image = [UIImage imageWithData:data]; 
     }]; 

    return cell; 
} 
+1

Я уверен, что 'dequeueReusableCellWithIdentifier' не имеет ничего общего с предотвращением утечек. Если есть утечка, это будет ошибка разработчиков, а не факт, что они использовали или не использовали этот метод. – Joe

+0

Действительно. Это не утечка, чтобы не использовать его. Помните, что утечка - это объект, на который больше ничего не ссылается. По своей природе ячейки * * ссылаются на табличное представление, потому что они возвращаются, если вызывается 'dequeueReusableCellWithIdentifier:'. – mattjgalloway

+0

Вы правы, моя проблема очень похожа, просто загружаю изображения из файлов, а не из сети, а [UIImage imageWithContentOfFile:] является узким местом ... Я попытаюсь адаптировать ваш код и посмотреть, могу ли я сделать он работает для моего дела. –

0

Вы должны использовать метод, всегда. Если он возвращает ячейку, используйте эту ячейку. Если он возвращает nil, тогда и только тогда вы должны создать ячейку.

Если метод требует ячейки для изображения, которое вы еще не загрузили, вам следует предоставить альтернативу. Возможно, UIImageView с настраиваемым анимированным прокруткой прогресса? Затем, когда ваше изображение прибывает, вы можете использовать надлежащее изображение, когда это необходимо.

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

2

dequeueReusableCellWithIdentifier помогает снизить количество загружаемых вами вещей из файлов или XIB. Если у вас есть пользовательская ячейка или ячейки с нестандартным содержимым, может быть намного быстрее повторить использование ячейки, у которой уже есть все настройки.

Я помню, как работала над чем-то подобным, но в нашем случае мы загружали изображения в объекты Core Data асинхронно и наблюдали изображение в этом объекте. Когда изображение было загружено, ячейка была уведомлена и обновила его изображение.

Когда ячейка вернулась к нам через dequeueReusableCellWithIdentifier, мы остановили ее от наблюдения и установили ее для наблюдения за изображением следующего объекта.

+0

Интересное решение: просто интересно, может ли обработка уведомлений быть «тяжелой», как воссоздание ячейки? Если вы используете UINib, это довольно быстро ... –

2

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

Смежные вопросы