UITableViewCell和UITableViewHeaderFooterView的重用
不管是系统自带的还是自定义的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的重用的更多相关文章
- 用xib自定义UITableViewCell的注意事项——重用
问题的提出: 有时候我们经常需要自定义tableView的cell,当cell里面的布局较为复杂时往往舍弃纯代码的方式而改用xib的方式进行自定义.当我们用纯代码的方式布局cell时,往往会在cell ...
- tableView中cell的重用机制
如果用UITableView显示成千上万条数据,就需要成千上万个UITableViewCell对象,然而OS设备的内存是有限的,这样就将耗尽iOS设备的内存.要解决这个问题,需要提到重用UITable ...
- 解决UITableView中Cell重用机制导致内容出错的方法总结
UITableView继承自UIScrollview,是苹果为我们封装好的一个基于scroll的控件.上面主要是一个个的 UITableViewCell,可以让UITableViewCell响应一些点 ...
- iOS开发之UITableView及cell重用
1.UITanleview有的两种风格 一种是Plain,一种是Grouped,可以从这里设置风格: 他们样式分别如下: Plain: Grouped: 2.tableView展示数据的过程: (1) ...
- 解决Cell重用内容混乱的几种简单方法,有些方法会增加内存
重用实现分析 查看UITableView头文件,会找到NSMutableArray* visiableCells,和NSMutableDictnery* reusableTableCells两个结构 ...
- ios UITableView中Cell重用机制导致内容重复解决方法
UITableView继承自UIScrollview,是苹果为我们封装好的一个基于scroll的控件.上面主要是一个个的 UITableViewCell,可以让UITableViewCell响应一些点 ...
- Cell的重用原理
iOS设备的内存有限,如果用UITableView显示成千上万条数据,就需要成千上万个UITableViewCell对象的话,那将会耗尽iOS设备的内存.要解决该问题,需要重用UITableViewC ...
- TableViewCell重影问题
UITableView继承自UIScrollview,是苹果为我们封装好的一个基于scroll的控件.上面主要是一个个的UITableViewCell,可以让UITableViewCell响应一些点击 ...
- ReactiveCocoa基础知识内容
本文记录一些关于学习ReactiveCocoa基础知识内容,对于ReactiveCocoa相关的概念如果不了解可以网上搜索:RACSignal有很多方法可以来订阅不同的事件类型,ReactiveCoc ...
随机推荐
- hdu 2716 Message Decowding
Message Decowding Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others ...
- 在eclipse中使用javap工具反汇编
1.配置 Run---external tools---external tools configurations 选择Program 新建javap运行方式 设置location.workspace ...
- C++学习笔记(九):作用域和命名空间
作用域 作用域规则告诉我们一个变量的有效范围,它在哪儿创建,在哪儿销毁(也就是说超出了作用域).变量的有效作用域从它的定义点开始,到和定义变量之前最邻近的开括号配对的第一个闭括号.也就是说,作用域由变 ...
- PowerDesigner 12.5 汉化破解完整版
PowerDesigner 12.5 汉化破解完整版 分类: ⑦ 综合 2011-08-09 14:59 2979人阅读 评论(0) 收藏 举报 破解wizardsybasetoolsshell扩展 ...
- IPAD之分割视图 SplitViewController
转载自:http://www.w3cschool.cc/ios/att-ios-ui-splitview-htm.html 1 分割视图的使用 分割视图是 iPad 的特定视图控制器用于管理两个视图控 ...
- SCOM MP中的PowerShell脚本
该脚本可在MP中运行 #param ($file,$threshold) #此处可定义file 和 threshold 参数,该参数可以从脚本外部接收 $threshold = 10 $file = ...
- Mac Pro更换SSD后,在Win7下启用ACHI的方法AHCI
在Mac Pro下更换SSD后,如果安装Win7,要将SSD改为AHCI模式是非常麻烦的.本文介绍如何将Mac的Win7下的SSD改为AHCI方式驱动,及几种常见问题的处理. 一.当Lion与Win7 ...
- 浅谈iOS中的视图优化
引言: 让我们来思考几个问题,你开发过的产品,它还有可以优化的地方吗?能增加它的帧率吗?能减少多余的CPU计算吗?是不是存在多余的GPU渲染?业务这点工作量对于越来越强大的设备面前显得微不足道,但作为 ...
- <item name="android:windowNoTitle">true</item> 不起作用。标题栏未被隐藏
改变style.xml 加入 <item name="android:windowNoTitle">true</item> 按理说可以把标题栏隐藏,但是当s ...
- ORM之PetaPoco入门(一)--Petapoco简介
1. ORM概括 1.1. ORM简介 ORM 对象-关系映射(Object/Relation Mapping,简称ORM),是随着面向对象的软件开发方法发展而产生的.面向对象的开发方法是当今企业级应 ...