использование iPhone-dequeueReusableCellWithIdentifier


Я работаю над приложением для 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 61

6 ответов:

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

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

Что касается идентификатора ячейки-вместо того, чтобы просто использовать "ячейку" для идентификатора и вместо использования уникального идентификатора, такого как OP, не могли бы вы использовать "идентификатор типа"? Например, если в моей таблице было 3 типа ячеек - один с очень сложным под-макетом, один с просто Style1 и Style2, Я должен идентифицировать эти три отдельно, а затем просто перестроить их, если dequeue появится 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", вам не кажется?

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

UITableViewCell *cell = [self cellForRowAtIndexPath:indexPath]

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