这篇文章主要内容是关于列表流畅度的优化。希望这篇文章能够给大家在列表流畅度优化方面带来一点点启示。
读者可将本体提及的优化手段或者原理应用到自己的项目中去。但是希望大家在优化过程中,要结合自己的项目具体问题具体分析,因为本文讨论的影响流畅度的因素,可能并不是你的应用流畅性不佳的瓶颈,根据我的经验,大部分流畅的问题都是业务逻辑导致的,反倒什么离屏渲染啊之类大家耳熟能详的流畅度的影响因素在实际项目中并没有想象的那么大。如果不经实地测量就盲目应用一些优化手段,可能会导致过度优化,事倍功半。
卡顿产生的原因
在总体原则篇中提到,五大原则中的其中一个就是要理解优化任务的底层运行机制,因为只有深入了解底层机制才能更好的有针对性的提出更优的解决方案,所以在进行列表流畅度优化前,我们一定要弄清楚一个view从创建到显示到屏幕上都经历了那些过程,在这些过程中那些方面可能会导致性能瓶颈,以及造成卡顿的底层原因是什么。
我们知道iOS设备大部分情况下,屏幕刷新频率是60hz(ProMotion下是120hz),也就是每隔16.67ms会进行一次屏幕刷新。每次刷新时,需要CPU和GPU配合完成一次图像显示。其主要流程如下:
应用内:
- 布局。CPU创建view,设置其属性(frame、background color等等)
- 创建backing images。setContents将一个image传給layer或者通过 drawRect:或
drawLayer:inContext绘制 - 准备。Core Animation将layer发送到render server前的一些准备工作,比如图片解码等。
- 提交。Core animation将layers打包通过 IPC (Inter-Process Communication)
发送到render server
应用外(render server):
- 设置用来渲染的OpenGL triangles(如果是有动画,还需计算动画layer的属性的中间值)。
- 渲染这些可见的triangles,将结果提交到视频缓冲区
- 视频控制器以60hz频率读取缓冲区内容显示到显示器,如果在16.67ms内没有完成提交,则会被丢弃。
从上面的图中可以看到,在view显示的过程中,CPU和GPU都各自承担了不同的任务,CPU和GPU不论哪个阻碍了显示流程,都会造成掉帧现象。所以优化方法也需要分别对CPU和GPU压力进行评估和优化,在CPU和GPU压力之间找到性能最优的平衡点, 无论过度优化哪一方导致另一方压力过大都会造成整体FPS性能的下降。而寻找平衡点的过程则因项目特点不同而不同,并没有一套通用的方法,需要我们用instrument等性能评测工具,根据实际app的性能度量结果去做优化,不能凭空乱猜。
CPU优化
我们先看table view在滑动过程中CPU占用的情况。
从上图可以看出,在滑动过程中CPU占用特点是:
滑动时CPU占用率高、空闲时CPU占用率底
主线程CPU占用高、子线程CPU占底
根据上述特点我们可以做如下优化:
预加载,空间换时间
为什么要预加载:
- 滑动时CPU占用过高,16.67ms内无法完成内容提交—>导致卡顿
- 滑动时CPU占用率高,但空闲时CPU占用率底—>CPU占用分布特点
- 利用CPU空闲时间预加载,降低滑动时CPU占用峰值—>解决卡顿
通过预加载我们希望达到的CPU理想占用效果如下:
预加载内容:
静态资源预加载
- 如何预加载:创建列表前找时机加载。如启动时、viewDidLoad、runloop空闲时等等
- 加载内容:缓存在磁盘的网络数据、图片、其他滑动时需要的耗时的资源
- 注意事项:在预加载带来的滑动性能提升和内存占用增加之间权衡
动态资源预加载
如何预加载:
在iOS10以后,UITableView和UICollectionView提供了预加载机制,iOS12开始prefeatching做了优化,不再与cell的加载同时并发进行,而是cell加载完成之后串行开始prefeatch,