不管是系统自带的还是自定义的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. 牛一网ecshop综合类模板(仿淘常州) for ecshop 2.7.3

    牛一网目前已经开发好这款ecshop综合类/多色系模板,已经开发好多种色系,本模板将会搭配多种颜色让用户选择,以适应 家电数码. 个护化妆. 生鲜水果 . 服饰鞋帽 . 图书音像 . 粮油食品.家具家 ...

  2. eclipse快捷键补充

    编辑相关快捷键 注释          Ctrl + / 快速修复    Ctrl + 1 删除当前行 Ctrl + d 格式化文档 Ctrl + Shift + f 插入空行    Shift + ...

  3. PHP魔术变量总结

    php手册里面的解释 __FUNCTION__   returns only the name of the function   返回的仅仅是函数的名称 __METHOD__   returns t ...

  4. java的向下转型

    class A{ public void fun1(){ System.out.println("A-->public void fun1()"); } public voi ...

  5. 如何让OpenSSL得到JKS格式的keystore中的public and private key

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...

  6. xcode4的环境变量,Build Settings参数,workspace及联编设置

    转自:http://www.cnblogs.com/lancidie/archive/2013/04/08/3007566.html 一.xcode4中的环境变量 $(BUILT_PRODUCTS_D ...

  7. c/c++中使用指针需要注意的问题

    一.使用指针的时候需要注意几点: 分配空间 初始化 释放 二.常见的错误有几种: 1)内存分配未成功,却使用了它      编程新手常犯这种错误,因为他们没有意识到内存分配会不成功.常用解决办法是,使 ...

  8. ubuntu安装mysql后不能远程访问的方法

    ubuntu安装mysql后不能远程访问的方法1.mysql>GRANT ALL PRIVILEGES ON *.* TO 'myuser'@'%' IDENTIFIED BY 'mypassw ...

  9. jquery处理单击和双击事件

    今天做div点击时,需要用到同一div的单击和双击事件,出现问题如下 例子: Html <body> <div id="div_1">单击双击我</d ...

  10. [React Native] Using the WebView component

    We can access web pages in our React Native application using the WebView component. We will connect ...