您的位置:首页 > 产品设计 > UI/UE

TableView的优化

2016-05-14 21:44 441 查看

UITableView的简单认识

UITableView最核心的思想就是UITableViewCell的重用机制。简单的理解就是:UITableView只会创建一屏幕(或一屏幕多一点)的UITableViewCell,其他都是从中取出来重用的。每当Cell滑出屏幕时,就会放入到一个集合(或数组)中(这里就相当于一个重用池),当要显示某一位置的Cell时,会先去集合(或数组)中取,如果有,就直接拿来显示;如果没有,才会创建。这样做的好处可想而知,极大的减少了内存的开销。

知道UITableViewCell的重用原理后,我们来看看UITableView的回调方法。UITableView最主要的两个回调方法是tableView:cellForRowAtIndexPath:和tableView:heightForRowAtIndexPath:。理想上我们是会认为UITableView会先调用前者,再调用后者,因为这和我们创建控件的思路是一样的,先创建它,再设置它的布局。但实际上却并非如此,我们都知道,UITableView是继承自UIScrollView的,需要先确定它的contentSize及每个Cell的位置,然后才会把重用的Cell放置到对应的位置。所以事实上,UITableView的回调顺序是先多次调用tableView:heightForRowAtIndexPath:以确定contentSize及Cell的位置,然后才会调用tableView:cellForRowAtIndexPath:,从而来显示在当前屏幕的Cell。

举个例子来说:如果现在要显示100个Cell,当前屏幕显示5个。那么刷新(reload)UITableView时,UITableView会先调用100次tableView:heightForRowAtIndexPath:方法,然后调用5次tableView:cellForRowAtIndexPath:方法;滚动屏幕时,每当Cell滚入屏幕,都会调用一次tableView:heightForRowAtIndexPath:、tableView:cellForRowAtIndexPath:方法。


什么是TableView的优化以及为什么要优化

CPU(中央处理器)和GPU(图形处理器) CPU主要从事逻辑计算的一些工作 GPU主要从事图形处理方面的工作

CPU和GPU的共同点:

都有自己的缓存体系 都有自己的数字和逻辑运算单元 都为了完成计算任务而设计

CPU和GPU的不同点:

CPU的核少但是核内空间非常大 能够处理复杂的逻辑

GPU核多但是每个核的空间相对较小 故而处理复杂逻辑的空间较少

针对CPU和GPU的上述不同,面对一个程序系统分配给CPU的往往是较为复杂的逻辑运算,分配给GPU的通常是图片等控件的操作

上述不同而导致的结果

当程序员为CPU编程时,倾向于用复杂的逻辑结构优化算法来减少计算任务的时间 Latency

为GPU编程->利用其能够处理海量数据的优势,来提高总的数据的吞吐量 来掩盖Latency

为什么优化

学术上:平衡CPU和GPU在工作上的压力,从而正确的使用CPU和GPU的资源,使他们均匀的负载 这样子使得FPS保持在60帧左右,最终使得用户体验更加美好

非学术上:掌握UITableView能够给面试加分

UITableView的具体优化

1)尽量使用cell的复用

使用cell的复用,可以减少内存的开销,没有开辟新的空间,也减少了一些计算量

2)对于不定高的cell 提前将每个cell的高度存入数组,出现一个cell的时候,直接从数组中拿出确切的高度即可,不用临时计算cell的高度

图文混排、评论

对于固定高的cell和不定高的cell同样适用

3)涉及网络请求加载数据在UITableView滑动结束的时候在进行加载数据(渲染)避免卡顿

UITableView继承自UIScrollView,继承了后者的方法

//滑动结束的方法
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate

//减速结束之后的方法
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView


if(self.tableView.dragging==NO&&self.tableView.decelerating==NO)在tableView必须实现的二个方法中(加载cell的方法中)将数据的渲染写在上述if语句中

4)对于tableView的自定义控件 尤其是UIImageView,尽量减少使用圆角,阴影等layer属性,尽量减少使用alpha(透明度)来设置透明度,(在项目开发中,让UI设计师设计原图就是带圆角的图) 阴影,圆角这些layer效果都是在GPU中完成的

当多个视图重叠时,GPU会进行合成渲染,而渲染的最慢的操作就是混合,因此当视图结构太过复杂,就会大量消耗GPU的资源,所以当一个空间本身是不透明,注意设定alpha为1,这样可以避免无用的alpha通道合成,降低GPU的负载

对控件设置cornerRadius后对其进行clip或mask操作时 会导致offscreenrendering这个也是在GPU中进行的 如果在滑动时 圆角对象太多 回到GPU的负载大增幅。

这时我们可以设置layer的shouldRasterize属性为YES,可以将负载转移给CPU 更彻底的是直接使用带圆角的原图

5)尽量使用懒加载

又称为延迟加载 实际上是重写某个对象的getter方法 原理:程序一开始并不对它进行初始化 而是在用到他的时候 才为他开辟内存供它使用

好处:

不必将创建的对象的代码全部写在ViewDidLoad中,代码可读性强

每个控件的getter方法,分别负责各自的实例化处理,代码彼此之间独立性强 松耦合

6)减少返回给的cell里面的处理逻辑和处理时间

7)设置每个cell的opaque属性 —-面试亮点

opaque意思是不透明的 浑浊的 有YES和NO二个结果

alpha 透明度

如果控件本身不透明,我们设置opaque为YES

8)分段加载数据

设置分页加载数据 也就是上拉刷新和下拉加载

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ContTableCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ContacterTableCell"];
if (!cell) {
cell = (ContTableCell *)[[[NSBundle mainBundle] loadNibNamed:@"ContacterTableCell" owner:self options:nil] lastObject];
}
NSDictionary *dict = self.dataList[indexPath.row];
[cell setContentInfo:dict];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
return cell.frame.size.height;
}

/*
这样写,在Cell赋值内容的时候,会根据内容设置布局,当然也就可以知道Cell的高度,如果100行,那就会调用100+页面Cell个数次tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath方法,而我们对Cell的处理操作,都是在这个方法里的!什么赋值、布局等等。开销自然很大,所以改进代码。
*/
/*
//改写后代码
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *dict = self.dataList[indexPath.row];
return [ContacterTableCell cellHeightOfInfo:dict];
}
*/


tableview的优化主要是把赋值和计算布局分离。这样让tableView:cellForRowAtIndexPath:方法只负责赋值,tableView:heightForRowAtIndexPath:方法只负责计算高度。两个方法尽可能的各司其职,不要重叠代码!两者都需要尽可能的简单易算。我们可以在获得数据后,直接先根据数据源计算出对应的布局,并缓存到数据源中,这样在tableView:heightForRowAtIndexPath:方法中就直接返回高度,而不需要每次都计算了。

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *dict = self.dataList[indexPath.row];
CGRect rect = [dict[@"frame"] CGRectValue];
return rect.frame.height;
}


UITableView的优化主要从三个方面入手:

提前计算并缓存好高度(布局),因为heightForRowAtIndexPath:是调用最频繁的方法;

异步绘制,遇到复杂界面,遇到性能瓶颈时,可能就是突破口;

滑动时按需加载,这个在大量图片展示,网络加载的时候很管用!(SDWebImage已经实现异步加载,配合这条性能杠杠的)。

到这里,优化后的代码基本能满足简单的界面!如果需要实现较为复杂的图文混排,还需要进一步优化。

如果项目是用的Xib、Storyboard,并需要优化UITableView的话,可以看看sunnyxx大神的方案:http://blog.sunnyxx.com/2015/05/17/cell-height-calculation/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息