iPhone - использование dequeueReusableCellWithIdentifier - PullRequest
61 голосов
/ 28 мая 2010

Я работаю над приложением для iPhone, которое имеет довольно большой UITableView с данными, взятыми из Интернета, поэтому я пытаюсь оптимизировать его создание и использование.

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

Вот что обычно делают люди:

UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];

if (cell == nil) {
  cell = [[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"Cell"];

// Add elements to the cell
return cell;

И вот как я это сделал:

// The cell row
NSString identifier = [NSString stringWithFormat:@"Cell %d", indexPath.row]; 

UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:identifier];

if (cell != nil)
  return cell;

cell = [[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:identifier];
// Add elements to the cell
return cell;

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

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

В порядке Я не знаю, какой из них лучший, «общий» метод сводит использование памяти таблицы к точному количеству ячеек, которые она отображает, в то время как метод, который я использую, похоже, поддерживает скорость сохраняет все вычисленные ячейки, но может привести к большому потреблению памяти (если не существует внутреннего предела для очереди).

Неправильно ли я использовать это таким образом? Или это зависит только от разработчика, в зависимости от его потребностей?

Ответы [ 6 ]

70 голосов
/ 28 мая 2010

Цель dequeueReusableCellWithIdentifier - использовать меньше памяти. Если экран может вместить 4 или 5 ячеек таблицы, то при повторном использовании вам нужно только выделить 4 или 5 ячеек таблицы в памяти, даже если таблица содержит 1000 записей.

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

19 голосов
/ 09 июня 2010

Что касается идентификатора ячейки. Вместо того, чтобы просто использовать «ячейку» для идентификатора и вместо использования уникального идентификатора, такого как OP, не могли бы вы использовать «идентификатор типа»? Например, если в моей таблице было 3 типа ячеек - одна с очень сложной подкомпоновкой, одна с Style1, а другая с Style2, я должен идентифицировать эти три по отдельности, а затем просто перестроить их, если возникнет деку до nil.

Например:

-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath{
    NSString* ident = @"";
    if(indexPath.section == 0) ident= @"complicated";
    if(indexPath.section == 1) ident= @"style1";
    if(indexPath.section == 2) ident = @"style2";

    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:ident];

    if(cell == nil){

       if(ident == @"complicated"){
          cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:ident] autorelease]; 
         // do excessive subview building
       }
       if(ident == @"style1"){
          cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyle1 reuseIdentifier:ident] autorelease]; 
       }

       if(ident == @"style2"){
          cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyle2 reuseIdentifier:ident] autorelease]; 
       }


    }
    if(ident == @"complicated"){
       // change the text/etc (unique values) of our many subviews
    }
    if(ident == @"style1"){
      [[cell textLabel] setText:@"Whatever"];
    }
    if(ident == @"style2"){
      [[cell textLabel] setText:@"Whateverelse"];
    }

    return cell; 
}

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

Я не думаю, что Apple создала бы идею многократного использования ячеек с идентификаторами, если бы они хотели, чтобы все идентификаторы были "cell", вам не кажется?

13 голосов
/ 02 августа 2011

Документация, которая помогла мне понять, почему идиоматический способ (тот, который вы описали вначале) работает лучше всего, это ссылка на класс UITableViewCell раздел по методу initWithStyle:reuseIdentifier:.

Подраздел reuseIdentifier гласит:

Вы должны использовать один и тот же идентификатор повторного использования для всех ячеек одной формы.

А подраздел "Обсуждение" гласит:

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

Эти утверждения дают мне понять, что идиоматический способ использования dequeueReusableCellWithIdentifier внутри вашей реализации tableView:cellForRowAtIndexPath: для вашего UITableViewDataSource создает один объект ячейки для каждой видимой строки независимо от общего количества. количество доступных строк.

4 голосов
/ 28 мая 2010

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

1 голос
/ 14 октября 2011

UITableView внутренне использует ячейку с идентификатором в качестве «шаблона». Поэтому в следующий раз, когда вы (читаете как таблица) попытаетесь удалить, он просто создаст новую ячейку, но с использованием сохраненного объекта в качестве шаблона. Следовательно, вам все еще нужно обновить его пользовательский интерфейс для отображения содержимого ячейки в соответствии с контекстом.

Это также означает, что UITableView выполняет управление памятью ячеек для нас как таковых. Теоретически, UITableViewCell объектов будет столько же, сколько видимых ячеек. Но практически может быть еще пара, ожидающая освобождения памяти.

Это в основном экономит память, особенно в сценариях, где у вас 1000 ячеек.

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

С другой стороны, если ваша ячейка является настраиваемой ячейкой, то, скорее всего, мы можем загрузить перо и извлечь из него. Если это так, вы можете использовать идентификатор для удаления или ИЛИ вы можете загрузить его из пера. В процедуре нет никакой разницы.

Единственная разница может быть во времени загрузки. Разрешение представления таблицы для создания новой ячейки с использованием ячейки идентификатора в качестве шаблона может быть немного быстрее, чем загрузка из пера, но это едва заметно и зависит от контекста.

0 голосов
/ 12 июля 2012

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

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

UITableViewCell *cell = [self cellForRowAtIndexPath:indexPath]

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...