Подкласс UITableViewCell создает зомби, если не сохранен

#iphone #uitableview #nszombie

#iPhone #uitableview #nszombie

Вопрос:

Я создал подкласс UITableViewCell с пользовательским указателем и использую его в двух разных UITableViews в моем приложении. Он отлично работает в одной из таблиц, но другая таблица выходит из строя, когда я ее интенсивно прокручиваю. Инструменты идентифицируют зомби в этом коде (в cellForRowAtIndexPath):

 NSString *identifier = @"edit";
LogTableCell *cell = (LogTableCell*)[tableView dequeueReusableCellWithIdentifier:identifier];

if (!cell) {
   cell = (LogTableCell*) [[[NSBundle mainBundle] loadNibNamed:@"LogTableCell" owner:self options:nil] objectAtIndex:0];        
   [cell retain];       // prevents zombies!
}
NSLog(@"%@: retainCount: %d", identifier, [cell retainCount]);

// some other cell init stuff

return cell;
  

Обратите внимание на строку [сохранить ячейку]; — когда она есть, код работает безупречно. Извлеките его и завершите работу. NSLog всегда сообщает о количестве удерживаемых файлов, равном 2, поэтому в этом не должно быть необходимости. Но если я сделаю что-то вроде этого:

    if ([cell retainCount] < 1) { [cell retain]; }       // does not prevent zombies!
  

это не работает. Нет alloc / init, поэтому мне не нужно выполнять автоматическое освобождение или вообще беспокоиться об этом, и я всегда думал, что cellForRowAtIndexPath освобождает ячейку для меня.

Когда я не использую инструменты, вот ошибка, которую я получаю от xcode:

 *** -[CALayer retain]: message sent to deallocated instance 0x4d8e930
  

Даже если это работает со строкой [cell retain]; , для анализа (и для меня) это выглядит как утечка, поэтому я хотел бы решить проблему. Кто-нибудь знает, что здесь происходит?

Комментарии:

1. Я понял это с помощью небольшого отслеживания. Проблема заключалась в освобождении UITableViewCell с подклассом UITableViewCell. У меня было несколько переменных экземпляра и несколько свойств, все из которых я освобождал в dealloc. Но некоторые свойства были @synthesized для некоторых из этих переменных экземпляра, и когда они объединены таким образом, их коллективно нужно освободить только один раз. Итак, я прокомментировал выпуски переменных экземпляра, которые были привязаны к свойствам, и это устранило проблему. Я надеюсь, что это кому-нибудь поможет!

2. Рекомендуется избегать использования свойств в вашем методе dealloc. Вызов свойства может иметь нежелательные побочные эффекты. Освобождение вашего ivar позволяет избежать любых потенциальных проблем.

3. Это не огромная проблема, многие люди выпускают переменные через свойства, и Apple даже делает это в некоторых своих примерах кода… но при прочих равных условиях, получите прямой доступ к iVar.

4. Спасибо за комментарий! Когда вы говорите «использование свойств в вашем методе освобождения», вы имеете в виду «освобождение свойств в вашем методе освобождения?»

5. Я имею в виду сказать: self.property = nil; вместо [property release];

Ответ №1:

Не вызывайте retainCount

Абсолютное количество сохранений бесполезно.

([cell retainCount] < 1) не может работать; retainCount никогда не может вернуть ноль.

(Да — загрузка кончика в cellForRowAtIndexPath: теперь благословлена фреймворком. Coolio.)

Тогда ваша проблема заключается в другом, поскольку этот код (без сохранения) верен.

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

Если вы запустите инструмент распределения и включите запись событий сохранения / освобождения, вы можете точно увидеть, где происходят вызовы для сохранения / освобождения / автоматического освобождения объекта. Событие, вызывающее сбой, имеет очевидную ценность.

В этом случае, однако, вам, вероятно, где-то не хватает пары сохранить / освободить. По крайней мере, это было бы исправлением симптомов. Реальная проблема, вероятно, заключается в каком-то переходе пользовательского интерфейса, который происходит таким образом, что часть содержимого фактически преждевременно извлекается, в то время как какой-то другой раздел вашего приложения все еще зависит от него. Добавление пары сохранения / освобождения таким образом, чтобы продолжительность жизни ячейки сохранялась при этом переходе, на самом деле не является исправлением, поскольку могут быть другие зависимости.

Комментарии:

1. Не должно быть никаких проблем с производительностью при выполнении пользовательского UITableViewCell таким образом. Я сделал что-то почти идентичное без снижения производительности, и это очень близко к рекомендации Apple .

2. Я делал это раньше, и я не видел никаких проблем с производительностью, что бы там ни было. Кроме того, я предположил, что retainCount никогда не сможет вернуть ноль, но я решил, что попробовать стоит.