不管是系统自带的还是自定义的UITableViewCell,对于它们合理的使用都是决定一个UITableView的性能的关键因素。应该确保以下三条:

  • UITableViewCell的重复利用:首先对象的创建,尤其是UI控件的创建,会带来性能损耗。假设在一个很短的时间内重复分配内存,比如用户滚动一个TableView的时候,如果我们可以重复利用一些之前创建的cell,而不是再次创建新的对象,这将显著提升UITableView的性能。

  • 避免对内容重新布局:当使用一个自定义的Cell时,避免在UITableView请求显示cell时针对cell的subViews重新布局(重新计算子控件的位置),要做到一次创建重复使用。

  • 使用不透明的subViews:当自定义自己的 cell时,让cell的subviews不透明。

同样的,针对UITableView的每一个Section,我们同样需要遵循上面的原则来对它的Header和Footer来重用。这两个View都是UITableViewHeaderFooterView类型。

下面我们讨论一下普通模式与缓存模式(可能不太恰当)的代码组织上的不同。

一般情况下我们可以像如下代码一样为指定的NSIndexPath创建一个UITableViewCell:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
/*
...可以在此处设置 cell的相关属性或者是子控件的信息
*/
return cell;
}

我们都知道UITableView显示时仅仅会为可见部分创建UITableViewCell以及UITableViewHeaderFooterView控件,当用户滚动 UITableView而导致其显示下方或上方的Section时,它都会重绘当前区域,那么意味着每一次的滑动操作可能都导致调用cellForRowAtIndexPath方法,从上面的代码来看,每次都会创建新的cell,当用户快速滑动时,这个创建过程尤其频繁,这或许是某些应用滚动时卡顿的原因之一。

当用户向上滚动并隐藏了之前创建的Section,然后再次向下滚动显示这些section时,系统会再次创建,这就意味着,之前创建的UITableViewCell或UITableViewHeaderFooterView没有被系统很好的利用起来,这些控件有无当做垃圾回收还有待验证。

我们知道创建一个控件的代价是非常大的,系统出于这方面考虑,提供了缓存机制,在 UITableView中维持了一个队列,用于缓存之前创建过的CellView或HeaderFooterView,缓存项都有一个唯一的标识Identifier来表示。用户可以通过标识从缓存出取得对应的控件。

既然这样,就有了下面的改进方案:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// static修饰局部变量:可以保证局部变量只分配一次存储空间(只初始化一次)
static NSString *ID = @"XXXX"; // 1.通过一个标识去缓存池中寻找可循环利用的cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; // 2.如果没有可循环利用的cell,再行分配
if (cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
}
/*
...可以在此处设置 cell的相关属性或者是子控件的信息。
*/
return cell;
}

使用上面的代码可以重复利用已有的控件。看起来都很美好,但是其实这里存在重复判断,[tableView dequeueReusableCellWithIdentifier:ID]是一个很智能的方法,方法内部首先从缓存中相应标识的控件,在返回前系统会执行一次判断是否nil的操作,如果为nil,系统尝试为你创建一个。这个时候系统会做如下操作。

   // 1.通过一个标识去缓存池中寻找可循环利用的cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; // 2.如果没有可循环利用的cell,调用初始化方法
if (cell == nil){
cell = [[UITableViewCell alloc] initWithReuseIdentifier:ID];
}

既然系统有了上面的逻辑,那么我们只需要覆写 super类的initWithReuseIdentifier:方法即可,例如如下代码片段:

- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier{
if (self = [super initWithReuseIdentifier:reuseIdentifier]) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeContactAdd];
[self.contentView addSubview:button];
self.button = button; [button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}

这也就意味着,代码现在可以简化,下面是某个小例子中的代码片段:(这里引用的是UITableViewHeaderFooterView的代码,原理其实一样。)

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{

   FriendGroupHeaderView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"FriendGroupHeaderView"];

   headerView.model = self.friendGroups[section];

   return headerView;
}

但是系统如何根据一个标识就知道创建哪一个控件呢,这就需要我们个告知系统在从缓存中读取失败后,如何创建这些控件,以下为截取的UITableView头文件的相关方法:

// Beginning in iOS 6, clients can register a nib or class for each cell.
// If all reuse identifiers are registered, use the newer -dequeueReusableCellWithIdentifier:forIndexPath: to guarantee that a cell instance is returned.
// Instances returned from the new dequeue method will also be properly sized when they are returned.
- (void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(5_0);
- (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0);
- (void)registerNib:(UINib *)nib forHeaderFooterViewReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0);
- (void)registerClass:(Class)aClass forHeaderFooterViewReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0);

系统提供了两种创建指定标识控件的方法。

1、对象类:

通过对象类创建的方法会自动调用 initWithReuseIdentifier:方法,这种模式一般用于自定义控件的子控件位置不固定或者是子控件不固定的场景,例如QQ聊天消息,每一行消息的高度是不一致的,针对是否会员还有可能选择是否创建皇冠图标。

2、Nib/Xib:

这个和通过Xib方法创建控件方式一样。适用于子控件固定的场景。

我们需要主动调用这些方法中的一个来告知系统,代码片段如下:

- (void)viewDidLoad {
  [super viewDidLoad];
  
  // 注册指定标示的控件创建方式
  [self.tableView registerClass:NSClassFromString(@"FriendGroupHeaderView") forHeaderFooterViewReuseIdentifier:@"FriendGroupHeaderView"];
}

UITableViewCell和UITableViewHeaderFooterView的重用的更多相关文章

  1. 用xib自定义UITableViewCell的注意事项——重用

    问题的提出: 有时候我们经常需要自定义tableView的cell,当cell里面的布局较为复杂时往往舍弃纯代码的方式而改用xib的方式进行自定义.当我们用纯代码的方式布局cell时,往往会在cell ...

  2. tableView中cell的重用机制

    如果用UITableView显示成千上万条数据,就需要成千上万个UITableViewCell对象,然而OS设备的内存是有限的,这样就将耗尽iOS设备的内存.要解决这个问题,需要提到重用UITable ...

  3. 解决UITableView中Cell重用机制导致内容出错的方法总结

    UITableView继承自UIScrollview,是苹果为我们封装好的一个基于scroll的控件.上面主要是一个个的 UITableViewCell,可以让UITableViewCell响应一些点 ...

  4. iOS开发之UITableView及cell重用

    1.UITanleview有的两种风格 一种是Plain,一种是Grouped,可以从这里设置风格: 他们样式分别如下: Plain: Grouped: 2.tableView展示数据的过程: (1) ...

  5. 解决Cell重用内容混乱的几种简单方法,有些方法会增加内存

    重用实现分析 查看UITableView头文件,会找到NSMutableArray*  visiableCells,和NSMutableDictnery* reusableTableCells两个结构 ...

  6. ios UITableView中Cell重用机制导致内容重复解决方法

    UITableView继承自UIScrollview,是苹果为我们封装好的一个基于scroll的控件.上面主要是一个个的 UITableViewCell,可以让UITableViewCell响应一些点 ...

  7. Cell的重用原理

    iOS设备的内存有限,如果用UITableView显示成千上万条数据,就需要成千上万个UITableViewCell对象的话,那将会耗尽iOS设备的内存.要解决该问题,需要重用UITableViewC ...

  8. TableViewCell重影问题

    UITableView继承自UIScrollview,是苹果为我们封装好的一个基于scroll的控件.上面主要是一个个的UITableViewCell,可以让UITableViewCell响应一些点击 ...

  9. ReactiveCocoa基础知识内容

    本文记录一些关于学习ReactiveCocoa基础知识内容,对于ReactiveCocoa相关的概念如果不了解可以网上搜索:RACSignal有很多方法可以来订阅不同的事件类型,ReactiveCoc ...

随机推荐

  1. Partition Array

    Given an array nums of integers and an int k, partition the array (i.e move the elements in "nu ...

  2. 5-17 Hashing (25分)

    The task of this problem is simple: insert a sequence of distinct positive integers into a hash tabl ...

  3. xampp配置host和httpd可以随意访问任何本机的地址

    1.修改host 不管你用的是什么系统,windows, mac,电脑上都会有一个 hosts 文件,修改这个文件,可以改变主机名所对应的 ip 地址.比如你安装了 Web 开发环境(MAMP 或 W ...

  4. NGUI学习笔记(一):官方视频学习记录

    学习NGUI一直断断续续的,目前打算做一个总结的笔记. 我使用的是比较老的3.6.0版本. 1.使用NGUI,需要开启“Edit”->“Project Settings”->“Physic ...

  5. Unity3D4.x之AssetBundle学习笔记

    关于AssetBundle AssetBundle可用来将多个资源打包为一个文件,实现动态下载和更新.需要注意的是Unity3D5.x以后对打包方式进行了升级,不用再在依赖关系上伤透脑筋,但是和4.x ...

  6. [Java 7][msvcr100.dll] Error when load Eclipse

    [Problem] After I updated to Java 7, I could not load Eclipse. Here comes the erros: Eclipse: eclips ...

  7. Cobar 关系型数据的分布式处理系统

    原文地址: http://code.alibabatech.com/wiki/display/cobar/Home;jsessionid=779959E690AE94BBC8079BB8F7D8B24 ...

  8. Android开发实例之多点触控程序

    智能终端设备的多点触控操作为我们带来了种种炫酷体验,这也使得很多Android开发者都对多点触控程序的开发感兴趣.实际上多点触控程序的实现并不是那么遥不可及,而是比较容易.本文就主要通过一个实例具体讲 ...

  9. ASC(1)G(上升时间最长的序列)

    G - Beautiful People Time Limit: 10000/5000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Other ...

  10. iOS开发——新特性OC篇&Objective新特性

    Objective新特性 Overview 自 WWDC 2015 推出和开源 Swift 2.0 后,大家对 Swift 的热情又一次高涨起来,在羡慕创业公司的朋友们大谈 Swift 新特性的同时, ...