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/
相关文章推荐
- MySQL 优化
- Google排名优化的几个影响因素
- DB2优化(简易版)
- Mysql limit 优化,百万至千万级快速分页 复合索引的引用并应用于轻量级框架
- C#中尾递归的使用、优化及编译器优化
- 对优化Ruby on Rails性能的一些办法的探究
- 优化Ruby脚本效率实例分享
- Asp编码优化技巧
- 如何监测和优化OLAP数据库
- mysql -参数thread_cache_size优化方法 小结
- 深入学习SQL Server聚合函数算法优化技巧
- MySQL常见的底层优化操作教程及相关建议
- 详解mysql的limit经典用法及优化实例
- 数据库学习建议之提高数据库速度的十条建议
- oracle数据库sql的优化总结
- SQL语句性能优化(续)
- SQL语句优化提高数据库性能
- SQL优化经验总结
- SQL优化技巧指南
- SQL Server优化50法汇总