不管是系统自带的还是自定义的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. 好的 ASP.Net网站、博客

    1.优秀的个人技术博客 http://www.tracefact.net/Asp-Net/

  2. HDU 3635 并查集+路径压缩+记录每个点移动次数

    题意: 给定n个点 oper个操作 每个点有1个龙珠 下面2种操作: T u v 把u点所有龙珠搬到v Q u  问u点当前所在城市 u点所在城市有几个龙珠 u点被移动几次 思路: 并查集可以求出 u ...

  3. 【STL源码学习】STL算法学习之一

    第一章:引子 STL包含的算法头文件有三个:<algorithm><numeric><functional>,其中最大最常用的是<algorithm>, ...

  4. java 学习基础学习单词及java关键词

    在JAVA学习中我们难免会犯一些逻辑错误,语法错误,和一些运行错误,对于英语不好的人,就的记下下面的2常用单词,有助于我们提高在使用软件编写代码的速度和代码调试,能更便捷的找出错误,知道1中的保溜关键 ...

  5. 实例:ABAP权限对象设计与权限检查的实现(详细)

    学习总结,分享给大家,,,(有图有真像) 我在ECC里创建了一张表,随意插入了5条数据 创建权限对象,使分配这个权限的用户只能操作部门编号(edept)为 ‘10’ 的数据. 1. SU20,创建权限 ...

  6. Genymotion与本地电脑共享文件夹的方法

    首先打开vbox的界面,左侧列表应该可以看到Genymotion添加的虚拟机,如图:对要设置的虚拟机单击右键,在弹出的菜单中点击“设置...”,弹出设置页面后点击左侧最后一个标签“共享文件夹”,点击右 ...

  7. 学习 MFC之 工具栏(二)

    对于InitToolBar()函数进行进一步解析: 1.首先声明一个全局对象:  CToolBar m_toolbar; 2.然后用create()创建toolbar: //创建ToolBar工具条 ...

  8. ThinkPHP CURD方法盘点:order方法

    order方法属于模型的连贯操作方法之一,用于对操作的结果排序. 用法 $Model->where('status=1')->order('id desc')->limit(5)-& ...

  9. materialish-progress

    https://github.com/pnikosis/materialish-progress materialish-progress-master.zip

  10. [MODx] 8. Snippet get data, chunk display

    Simple Example: Lets process this chunk and output its value. We have this Chunk, called "Welco ...