AsyncDisplayKit 是 Facebook 开源的用于保持 iOS 界面流畅的库。
-
ASDK 的基本原理
ASDK 认为,阻塞主线程的任务,主要分为以上三大类,文本和布局的计算、渲染、解码、绘制都可以通过各种方式异步执行,但 UIKit 和 CoreAnimation 相关操作必须在主线程执行。ASDK 的主要任务,就是将这些任务从主线程挪走,而挪不走的,就尽量封装优化。
为了达成这一目标,ASDK 尝试对 UIKit 组件进行封装
这是常见的 UIView 和 CALayer 的关系:UIView 持有 Layer 用于显示,View 中的大部分显示属性实际上是从 Layer 映射而来的;Layer 的 delegate 是 UIView,当其属性改变、动画产生时,View 能够得到通知。UIView 和 CALayer 不是线程安全的,并且只能在主线程创建、访问和销毁。
![](http://upload-images.jianshu.io/upload_images/4653622-ace8647c111ee2d1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/500)
ASDK 为此创建了 ASDisplayNode 类,包括了常见属性(比如 frame/bounds/alpha/transform/backgroundColor/superNode/subNodes)等,然后它用 UIView->CALayer 的方式,实现了 ASNode->UIView 这样的一个关系。
当不需要响应触摸事件时,ASDisplayNode 可以被设置为 layer backed,即 ASDisplayNode 充当了原来的 View 的功能,节省了更多资源。
与 UIView 和 CALayer 不同,ASDisplayNode 是线程安全的,它可以在后台线程创建和修改。Node 刚创建的时候,并不会在内部新建 UIView 和 CALayer,直到第一次在主线程访问 UIView 和 CALayer 属性时,它才会在内部生成相应对象。当它的属性(frame/transform)改变后,它并不会立即同步到它持有的 View 或者 Layer 上,而是把改变的属性保存到内部的一个中间变量,稍后需要的时候再通过某个机制一次性设置到内部的 View 和 Layer。
ASDK 的图层预合成
有时候一个 Layer 会包含许多 sub-Layer,而这些 sub-Layer 并不需要响应触摸事件,也不需要进行动画和位置调整。ASDK 为此实现了一个叫做 pre-composing 的技术,可以把这些 sub-layer 合成渲染为一张图片。开发时,ASNode 已经替代了 UIView 和 CALayer;直接使用各种 Node 并设置为 layer backed 后,ASNode 甚至可以使用预合成来避免创建内部的 UIView 和 CALayer。
通过这种方式,把一个大的层级,通过一个大的绘制方法绘制到一张图上,性能会获得很大提升。CPU 也避免了创建 UIKit 对象的资源消耗,GPU 避免了多张 Texture 合成和渲染的消耗,更少的 bitmap 也意味着更少的内存占用。ASDK 异步并行开发
自4S 开始,苹果移动设备都已经是双核 CPU 以上 ,充分利用多核的优势、并发执行任务对保持界面流畅有很大作用。ASDK 把布局计算、文本排版、图片/文本/图形渲染等操作都封装成较小的任务,并利用 GCD 异步并发执行。如果开发者使用了 ASNode 相关的控件,那么这些并发操作会自动在后台进行,无需进行过多配置。-
Runloop 任务分发
Runloop Work Distribution 是 ASDK 一个比较核心的技术。ASDK 的介绍视频和文档中都没有详细介绍,但是网上关于 Runloop 的博客很多,在这里无需赘述。
iOS 的显示系统是由 VSync 信号驱动的,VSync 由硬件时钟生成,每秒发出60次(这个值取决于设备,iPhone 上通常是 59.97 次)。iOS 图形服务收到 VSync 信号后,会通过 IPC 通知的 App 内,App 的 Runloop 在启动后会注册对应的 CFRunloopSource 通过 math_port 传过来的时钟信号通知,随后 Source 的回调会驱动整个 App 的动画与现实。
Core Animation 在 Runloop 中注册了一个 Observer,监听了 BeforeWaiting 和 Exit 事件,这个 Observer 的优先级是 200 0000,低于其他常见的 Observer。当一个触发事件到来时,Runloop 被唤醒,App 中的代码会执行一些操作,比如创建和调整视图层级
设置 UIView 的 frame、修改 CALayer 的透明度、为视图添加一些动画;这些操作最终会被 CALayer 捕获,并通过 CATransaction 提交到一个中间状态去(CATransaction 的文档中有提到这写内容,但并不完整)。当上面的所有操作结束后,Runloop 即将进入休眠或退出时,关注该事件的 Observer 都会得到通知,这时 CA 注册的那个 Observer 就会在回调中把所有的中间状态合并提交到 GPU 去显示;如果此处有动画,CA 会通过 DisplayLink 等机制多次触发相关流程。
ASDK 在此处模拟了 CoreAnimation 的这个机制,所有针对 ASNode 的修改和提交,总有些任务必须放到主线程中去执行的。当出现这种任务的时候,ASNode 会把任务用 ASASyncTransaction(Group)封装并提交到一个全新容器中去。ASDK 也在 Runloop 中注册了一个Observer,监听的事件和CA一样,但是优先级比 CA 要低。在 Runloop 进入休眠前,CA 处理完事件后,ASDK 就会执行该 loop 内提交的所有任务。具体代码见ASAsyncTransactionGroup。
通过这种机制,ASDK 可以在合适的机会把同步、异步的操作同步到主线程中去,并且能获得不错的性能。
- 其他
ASDK 中还封装了许多高级的功能,比如滑动列表的预加载、v2.0 添加新的布局模式等。
ASDK 是一个很庞大的库,它本身并不推荐你将整个 App 全部改为 ASDK 驱动,把最需要提升交互性能的地方用 ASDK 进行优化就足够了。