Использование 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;

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

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

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

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

Ответ 1

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

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

Ответ 2

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

Ответ 3

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

В подразделе reuseIdentifier говорится:

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

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

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

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

Ответ 4

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

Ответ 5

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

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

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

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

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

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

Ответ 6

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

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

UITableViewCell *cell = [self cellForRowAtIndexPath:indexPath]

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