前段时间在自己写新浪微博项目时,用到了自定义的TableViewCell和UITableView控件。由于微博的数目较多,且每条微博中包含大量的图片,如果对每条微博都新建一个cell进行展示的话,势必会耗尽系统资源并最终导致应用闪退。这里总结一下项目中用到的UITableView性能优化的一些知识点。
一、UITableViewCell的介绍和优化
1、UITableView中的每一行都是一个UITableViewCell,通过数据源(dataSource)的-(UITableViewCell *)tableview:(UITableView *)tableview cellForRowAtIndexPath:(NSIndexPath *)indexPath方法来初始化表中的每一个cell。
UITableViewCell内部有个默认子视图:contentView,它是UITableViewCell中所显示的所有视图的父视图,可用来设置显示一些辅助指示视图(通过设置UITableViewCell的accessoryType来显示),也可以通过accessoryView来自定义辅助指示视图(如在cell的右边放置一个自定义图片)。
2、Cell的重用
当滚动table时,会有一些cell移出屏幕显示范围,同时有一些cell进入屏幕中显示。这时,UITableView会将移出屏幕的cell放入一个对象池中并等待重用。当UITableView要求数据源返回cell时,数据源会首先从这个对象池中取出未被使用的cell并用新的数据来配置这个cell,然后返回给UITableView。从而使得需要创建的cell数量维持在很低的水平,避免了过多地创建cell对象而消耗系统资源。
注意:当我们需要使用自定义cell,而UITableView中每一行使用的不是同一种cell时,对象池中会存在很多种不同类型的cell。此时,需要用到UITableViewCell的reuseIdentifier属性,在初始化cell时传入一个特定的字符串标识符(NSString *)来区分不同类型的cell。数据源收到请求后会首先根据该标识符在对象池中查找相应类型的cell,如果有,就重用该cell,否则就使用传入的标识符来初始化这个UITableViewCell对象。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// 1、定义可重用标识符
static NSString *CellIdentifier = @"Cell";
// 2、先去对象池中查找是否有满足条件的Cell
StatusCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
if (cell == nil) { // 3、如果对象池中没有符合条件的cell,就自己创建一个
// 4、创建cell, 并且设置可重用标识符
cell = [[StatusCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
cell.delegate = self;
}
// 5、给cell设置数据
cell.cellFrame = _statusFrames[indexPath.row];
return cell;
}
二、进一步优化
当自定义cell中显示很多图片,而且这些图片都是从网络上加载的时候,单单使用重用cell的方法,滑动table时还是会出现卡顿的现象。那么,异步加载可以很好地解决这个问题,但是,如果对每个图片都使用异步加载,势必会造成应用中的线程数过多,这样也会造成卡顿(比如在下滑时异步加载图片的情况)。
从我用过的几款APP中,我发现了这样一个现象,这些APP在快速滑动table时,图片并不会显示(只是显示一幅占位图片),只有当滑动快结束或者用手指强行结束table的滑动时,图片才会加载并显示出来。这样,也许是一个好的优化思路。
我们知道,UITableView继承自UIScrollView,我们可以使用UIScrollViewDelegate的
-(void)scrollViewDidEndDragging:(UISrollView *)scrollView willDecelerate:(BOOL)decelerate
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
这两个方法来识别UITableView的结束滑动和减速滑动结束时开始进行异步加载图片。
使用[self.tableView indexPathsForVisibleRows]方法获取当前可见的indexPaths并对这些cell进行异步加载。
同时,还需要在cellForRowAtIndexPath方法中对cell的绘制做出限制:
if((!self.tableView.dragging)&&(!self.tableView.decelerating))
{
// 开始异步加载并设置数据
}