【问题标题】:iPhone - dequeueReusableCellWithIdentifier usageiPhone - dequeueReusableCellWithIdentifier 用法
【发布时间】:2011-02-25 02:41:00
【问题描述】:

我正在开发一个 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;

不同之处在于人们对每个单元格使用相同的标识符,因此将一个出列只会避免分配一个新的。

对我来说,排队的目的是给每个单元格一个唯一的标识符,所以当应用程序请求一个它已经显示的单元格时,既不需要分配也不需要添加元素。

很好我不知道哪个最好,“通用”方法将表格的内存使用量限制为它显示的确切单元格数,而我使用的方法似乎有利于速度它保留所有计算的单元格,但会导致大量内存消耗(除非队列有内部限制)。

这样使用我错了吗?还是仅取决于开发人员的需求?

【问题讨论】:

    标签: iphone objective-c optimization uitableview


    【解决方案1】:

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

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

    【讨论】:

    • 你说得对,使用我的方法,数组可以完成这项工作。大约 100 个单元是否代表一次分配的内存量“太大”?
    • 嗯,我改成常用的方法了(当一行有很多子视图时,这很复杂),而且除了较低的内存消耗外,滚动整体看起来更流畅,我不知道为什么。无论如何感谢您的建议!
    • @progrmr ...谢谢...为清除概念..它真的很好...我也犯了同样的错误..:)
    • 这并不完全正确。原因是 UITableViewCell (及其所有子视图)的副本比它的全新实例更快。关于内存使用的部分由细胞在不再可见时被释放的事实来处理。出列(=从模板对象复制)单元格只是加快了单元格创建的过程。因此,这与内存无关,而是与处理时间和使用的 CPU 资源(影响电池寿命等)有关。您根本不会保存任何实时内存空间 - 单元格仍然是单独的对象。
    • @manmal 这也是关于内存的。离开屏幕的细胞不会被释放,而是被重新用于变得可见的细胞。
    【解决方案2】:

    至于单元格标识符-您可以使用“类型标识符”而不是仅使用“单元格”作为标识符,而不是使用像 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",我认为他们不会创建带有标识符的整个可重用单元的想法,你不觉得吗?

    【讨论】:

    • 在你的情况下,我会为每个单元格布局使用一个标识符。这意味着一个用于“复杂”单元格,一个用于“style1”单元格,第三个用于 style2 单元格,如果它们的子视图与 style1 不同。在 dequeue 返回 nil 的情况下,使用标签(在枚举或其他东西中定义)添加单元格的子视图,然后初始化它们。如果出队返回一个单元格,只需使用标签检索子视图并更改它们。用一种方法为每个部分分离你的代码也很好:)
    【解决方案3】:

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

    reuseIdentifier 小节内容如下:

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

    “讨论”小节内容如下:

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

    这些语句让我清楚地知道,在 tableView:cellForRowAtIndexPath: 的实现中为 UITableViewDataSource 使用 dequeueReusableCellWithIdentifier 的惯用方式会为每个 visible 行创建一个单元格对象,而不管可用的总行数。

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 如果用户向下滚动然后向上滚动,那么内存将被重用......或者任何时候 tableView 访问一个显示一次然后隐藏的单元格。
      • 当然,但是对于曾经显示的每个单元格都会有内存占用,而不仅仅是当前显示的 4-5 个单元格。我对地图的注释也有类似的问题。切换到常量标识符会显着提高性能。
      • 那我想这取决于我......缓存我用来创建单元格的任何数据而不是单元格本身会更好吗?
      【解决方案5】:

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

      这也意味着UITableView 本身正在为我们进行单元的内存管理。理论上,UITableViewCell 对象的数量与可见单元格一样多。但实际上,可能还有更多等待释放的内存。

      这基本上可以节省大量内存,尤其是在您有 1000 个单元格的情况下。

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

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

      唯一的区别可能在于加载时间。允许 Table 视图使用标识符单元格作为模板创建一个新单元格可能比从 nib 加载稍快,但几乎不明显,并且取决于上下文。 p>

      【讨论】:

      • 自定义单元格也由 tableView 的内存队列系统管理,只要它们有一个Identifier 集。
      【解决方案6】:

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

      即使在所有这些之后你被卡住并且仍然需要获取单元格,那么你可以尝试以下代码

      UITableViewCell *cell = [self cellForRowAtIndexPath:indexPath]

      而应尽量避免,因为它会生成单元格的副本,但不会返回现有单元格,而内​​容将具有相同的值。

      【讨论】:

        猜你喜欢
        • 2014-11-07
        • 1970-01-01
        • 2013-08-19
        • 1970-01-01
        • 2016-06-29
        • 2023-03-29
        • 2014-12-01
        • 1970-01-01
        • 2014-09-27
        相关资源
        最近更新 更多