(ASDK已更名Texture)node
说到视图性能,不能不提到UITableView,对于它的滚动性能的讨论和优化从未中止。在咱们的探索过程当中,尝试过如下一些措施:后端
还有一些诸如圆角、opaque等普通UIView可能遇到的性能瓶颈已经在第一篇中讨论了一些,这里再也不赘述。缓存
然而咱们会想,cell的异步布局、图片和文字渲染是否还能够优化,预加载是否能够更完善更智能?仍然有许多问题等待被解决。性能优化
咱们先看一下通常UITableView加载Cell的过程:session
这些操做都在cell将要进入window的一刹那发生,不难理解,在短短的16ms里(60fps)是很难完成这些任务的,尤为是当用户快速滚动的时候,大量任务堆积在runloop,状况变得雪上加霜。多线程
若是将滚动中的CPU占用状况用图表显示出来,大概是这样的(WWDC16 session 219):
app
这其中每当一个cell将要进入屏幕,一个尖峰就会产生。而在其余相对空闲的时候,cpu的负载至关的低。框架
天然咱们会想到,若是把任务平均分配到一个时间段内,而不是集中在某一个点,是否就能够避免这样的状况发生?若是咱们可以预测一个cell很快将要进入屏幕,而此时cpu空闲,是否能够未雨绸缪,提早作一些布局和渲染的工做?那样一来,在cell真正须要显示的时候,因为布局和渲染结果backing store已是现成的了,只须要将它送给真正负责显示的view就能够,也就能够避免产生剧烈的性能波动。异步
首先的好消息是,做为ASDK的一员,ASTableNode以及其cellNode已经具有了异步布局和异步渲染的能力,即便没有作额外优化,仅仅利用ASDK通用的异步机制将耗时操做延后,相对于通常UITableView已经有了显著的提高。虽然性能锯齿仍然存在,可是将其转移到了后台线程之后,用户感觉到的卡顿就已经不会那么明显了。ide
然而这些彷佛还不够,在进入屏幕以后才开始渲染,会有短暂的白屏现象(等待渲染完成)再显示内容。既然渲染工做能够在显示以后再进行,那么相似的,也能够在显示以前的一段时间,把布局和渲染的工做预先完成。
要达到这些目的,首先介绍一些相关的类:
对于每个cell而言,本来须要在同一时间点进行的全部初始化/加载/布局/渲染等工做,如今被均匀分配到了不一样的状态进行预处理。随着用户滚动列表,根据cell离屏幕的距离不一样,设置相应的interfaceState并触发不一样阶段的工做,达到均匀分配的目的。同时,因为不须要在主线程上进行,多个cell的工做能够经过共享后台线程来大幅提升并行效率。
ASRangeController,一样与ASTableNode一一对应,而且能够根据设备性能自定义布局、加载、渲染的工做indexPath区间,在滚动时动态高效地调整各cell的interfaceState来层层触发不一样显示阶段的工做,对于流畅滚动起到了相当重要的做用。
ASScrollDirection,定义了列表滚动的方向(上下左右)。在ASRangeController调整各阶段的工做区间时,通常在用户滚动的方向上须要多加载一些,而滚出屏幕的cell在必定时间内回到屏幕的几率较低,所以其分配到的资源也就相应少一些。
在ASDK1.x的时代,因为彼时尚未ASRangeController的存在,cell的渲染只会在进入屏幕之后进行,也就是说,虽然性能可以达到60fps,可是滚动较快时,渲染跟不上,『白屏』现象就出现了。到了2.x有了ASRangeController以后,虽然在滚动极快的状况下仍然会由于资源不足而产生白屏现象,可是在通常状况下,由于资源分配更加合理,这个问题获得了显著的改善。
当同时layout多个node时,如何均匀分配工做到各个线程,同时单次不占用过多cpu时间?
ASDK是这么作的:
获取当前设备上cpu的数量,并乘以每一个cpu的工做量,如4 * 5 = 20,即同一批最多对20个node进行布局。(尽管没有找到严格的文档来讲明这样的计算方式会带来最高的效率,可是应该要比不分批次处理更优,使占用的cpu时间片可控)
调用dispatch_apply,对同一批次20个node进行并行布局计算
每一批处理20个,直到全部都处理完
不光ASDisplayNode自己会根据不一样的state进行相应的工做,它同时也提供了一系列的方法供子类override,如didEnterPreloadState/didExitVisibleState等等。在实际应用中,因为子类一般会持有一些本身管理的资源(如图片),须要控制在显示/离开屏幕之际进行资源的分配/回收。因为每一个时间点分的比较细,只要将工做均匀、合理地分配到相应的方法中,就能够实现很是精确高效的资源调度。
ASRangeController常常须要管理屏幕外的node(可能同时有好几屏的内容同时进行布局计算和显示),经过预处理来减轻未来的工做量,是一个典型的『空间换时间』的办法,对于内存的压力天然就会上升。
为此,ASRangeController提供了一些参数,让开发者能够自行决定每一个stage所囊括的范围,达到控制内存的:
ASLayoutRangeModeFull,此时用到的资源较多,同时用户体验也是最好的
ASLayoutRangeMinimum,比上一种类型节省一些资源
ASLayoutRangeModeVisibleOnly,在app退到后台的时候自动设置,将屏幕外的node所占用的资源释放,下降app在系统中被杀的几率
ASLayoutRangeModeLowMemory,比上一种更省内存,对于app退到后台,而且目前没有在屏幕上(可能在navigation stack里)的rangeController适用,最大程度释放资源
咱们能够经过调用setTuningParameters的方式,对于每一种mode中的每一种layoutRangeType作出精细调整。同时,ASDK会自动根据此时node在屏幕上的状况,自动在以上几种mode中来回切换,并根据指定的参数范围来加载/释放资源,达到资源和性能的平衡。
ASDK中还自带了一个显示rangeController工做状况的小工具:
即刻在热门页面有4个tab,相应的右下角的debug view显示的分别是4个页面不一样的working range。箭头表示当前的滚动方向,因为咱们配置了在滚动方向上加载的距离比反方向要更长,所以能够看到在滚动方向上的色块会更长一些。
即刻iOS在最初时也曾苦苦找寻列表性能优化方案。在阅读了Ray Wenderlich的一篇文章以后,惊艳其出色的性能,开始接触ASDK。最开始尝试在消息页面使用,后来在长期实践中,发现其确实可以解决tableView的行高计算和性能问题,才渐渐在其余页面使用。
就如同在本系列第一篇提到的,对于先进的框架虽然不能重度依赖,咱们也愿意敢于拥抱其先进之处。站在ASDK的角度看UITableView,就能有更大的空间来从新审视列表滚动的本质,将性能和资源分配效率提高到一个新的高度;即便未来脱离ASDK,仍然有一些想法值得咱们进一步思考。
想体验即刻的朋友能够下载看一下,最近又增长了很多新玩法 :)
PS: 咱们在招Android、爬虫、后端的职位,连接:jike.ruguoapp.com/careers