iPhone - dequeueReusableCellWithIdentifier用法

时间:2010-05-28 12:21:40

标签: iphone objective-c optimization uitableview

我正在开发一款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 个答案:

答案 0 :(得分:70)

dequeueReusableCellWithIdentifier的目的是减少使用内存。如果屏幕可以容纳4或5个表格单元格,那么重复使用时,即使表格有1000个条目,也只需要在内存中分配4或5个表格单元格。

在第二种方式中没有重复使用。第二种方式没有优势,只使用一组表格单元格。如果您的表有1000个条目,那么您将在内存中分配1000个单元格。如果你打算这样做,你会把它们放在一个数组中,然后用行号索引数组并返回单元格。对于具有固定单元的小型表,这可能是一个合理的解决方案,对于动态或大型表,这不是一个好主意。

答案 1 :(得分:19)

对于单元标识符 - 您可以使用“类型标识符”,而不是仅使用“单元格”作为标识符,而不是使用像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; 
}

(这段代码可能不会运行,因为我在这里写了,但希望你能得到这个想法。)

如果他们想要所有标识符为"cell",我认为Apple不会用标识符创建整个可重用的单元格想法,你不觉得吗?

答案 2 :(得分:13)

帮助我理解为什么惯用方式(您首先描述的方式)效果最好的文档是initWithStyle:reuseIdentifier:方法的UITableViewCell class reference部分。

reuseIdentifier小节的内容如下:

  

您应该对同一表单的所有单元格使用相同的重用标识符。

“讨论”小节的内容如下:

  

重用标识符与具有相同常规配置,减去单元格内容的表视图的那些单元格(行)相关联。

这些陈述让我清楚地知道,在dequeueReusableCellWithIdentifier的{​​{1}}实现中使用tableView:cellForRowAtIndexPath:的惯用方法会为每个可见 row,与可用的总行数无关。

答案 3 :(得分:4)

我认为第一个是实现UITableView的最佳方式(正如你所说的那样)。 使用第二种方式,将为显示的每个新单元分配内存,不会重复使用内存。

答案 4 :(得分:1)

UITableView在内部使用标识符为“模板”的单元格。所以下次你(读作表)尝试deque时,它只是创建一个新单元格,但使用存储的对象作为模板。因此,您仍然需要更新其UI以根据上下文反映单元格内容。

这也意味着UITableView本身正在为我们进行单元格的内存管理。从理论上讲,只有那么多UITableViewCell个物体和可见细胞一样多。但实际上,可能还有一些等待释放内存。

这基本上节省了大量时间,特别是在你有1000个单元格的情况下。

在内存非常宝贵的任何便携式设备上,我们应该将任何内存的分配推迟到最后一刻,并在其工作完成时释放它。 dequeAndReusing一个细胞实现了这一目标并且做得很好。

另一方面,如果您的单元格是自定义单元格,那么我们很可能会加载一个笔尖并从中提取。 如果是这种情况,您可以使用标识符来取消或者可以从笔尖加载它。手术没有区别。

唯一的区别可能是加载时间。允许表格视图使用标识符单元格作为模板创建新单元格 可以 比从笔尖加载要快一些,但它几乎不会引人注意,取决于上下文。

答案 5 :(得分:0)

要区分单元格与其他单元格,您可以使用单元格的tag属性,或者如果您正在使用自定义单元格,那么在对UITableViewCell进行子类化时,通过向自定义单元格引入任何新属性非常容易。

即使在所有这些之后你仍然需要获取单元格,然后你可以尝试使用代码

UITableViewCell *cell = [self cellForRowAtIndexPath:indexPath]

虽然它应该被避免达到一定程度,因为它产生了单元格的副本,但是没有返回现有的单元格,而内​​容将具有相同的值。