[译] Core Animation 编程指南 - 提高动画性能

1,086 阅读5分钟

对基于 app 的动画,Core Animation 是提高帧速率很好的方式,但并不是使用它一定能提高性能。尤其在 OS X ,你仍必须做出选择关于使用 Core Animation 行为最高效的方法。所有性能相关的问题,你应该使用 Instruments 随时间的推移去测量、追踪你 app 中的性能,这样你就能确定性能确实提高了,而不是更慢了。

给你的 OS X 视图选择最好的重绘策略

NSView 类默认的重绘策略是保存类的原始绘制行为,即使视图是图层支持视图。如果你在 app 中使用图层支持视图,你应该检查重回策略,选择能为你 app 提供最好性能的方式。在多数情况下,默认的策略并不是能提供最好性能的。而 NSViewLayerContentsRedrawOnSetNeedsDisplay 策略更可能减少你 app 的绘制数量、提升性能。其它策略可能对具体类型的视图会提供更好的性能。

关于视图重绘策略的更多信息,请参见OS X 视图的图层重绘策略会影响性能

更新 OS X 的图层去优化你的渲染路径

在 OS X v10.8 及以后版本,视图有两个选项来更新底部图层的内容。你在OS X v10.7及以前版本更新图层支持视图时,图层将视图 drawRect: 方法中的绘图命令捕获到背景位图图像中。缓存绘制命令是高效的,当它不是在所有情况下都是最高效的。如果你知道如何直接提供图层的内容,而不用真正地渲染它们,你可以使用 updateLayer 方法去实现。

关于渲染的不同路径的更多信息,包含涉及 updateLayer 方法,请参见使用委托提供图层内容

提示和技巧概述

这有几种方法使你的图层实现更加高效。然而,与之前的优化一样,在尝试优化之前,你始终应该测量代码的当前性能。 这为你提供了基准线,你可以使用它来确定优化是否有效。

尽量使用不透明的图层

将图层的 opaque 属性设置为 YES ,让 Core Animation 知道它不需要操作图层的 alpha 通道。没有 alpha 通道意味着合成器不需要将图层内容和背景内容混合,这会在渲染期间节省时间。然而,该属性主要与作为图层支持视图的一部分的图层,或 Core Animation 创建底层位图的情况相关。如果你直接给图层的 contents 属性赋值图像,图像的 alpha 通道都会被保留,不管 opaque 属性的值是什么。

对 CAShapeLayer 对象使用简单路径

CAShapeLayer 类通过将你提供的路径在合成时渲染成位图图像,来创建它的内容。这样做的优点就是图层总是以最好的分辨率绘制路径,但是这个优点也造成了额外的渲染时间。如果你提供的路径是复杂的,光栅化路径可能会非常耗时间。如果图层的尺寸经常变化(肯定会经常重绘),花费在绘制上的时间会累积起来,成为性能瓶颈。

减少图形图层绘制时间的方法就是将复杂图形分割成简单图形。合成器使用简单路径去绘制多个 CAShapeLayer 对象并拼接在一起,比绘制一个复杂路径要快得多。这是因为绘制操作发生在 CPU 上,而合成发生在 GPU 上。然而,与这种性质的任何简化一样,潜在的性能提升取决于你的内容。因此,在优化之前测量代码性能是非常重要的,这样你就有比较的基准线了。

为相同图层显式设置图层内容

如果你在多个图层对象上使用同一图像,需自己加载图像并直接将它赋值给图层对象的 contents 属性。给 contents 属性赋值图像可防止图层为后备存储分配内存。相反,图层会使用你提供的图像作为它的后备存储。当几个图层使用同一图像时,这意味着所有图层共享同一内容,而不是它们自己再拷贝图像。

始终设置图层尺寸为整型数值

为了得到最好结果,你始终应该将你图层对象的宽高设置为整型。虽然你使用单精度数字给图层对象的宽高指定值,但图层边界最终用于创建位图图像。为宽高指定整数值简化了 Core Animation 创建和管理后备存储和其他图层信息必须做的工作。

需要时,使用异步图层渲染

你在你 delegate 的 drawLayer:inContext: 方法,或者你视图的 drawRect: 方法所做的任何绘制,通常都是在你的 app 主线程同步执行的。然而,在某些情况下,同步绘制你的内容可能不会提供最好的性能。如果你发现你的动画执行效率并不是特别好,你应该试着使图层的 drawsAsynchronously 属性生效,去将操作移到后台线程。如果你这样做了,请确保你的代码是线程安全的。并且,你始终应该在将异步绘制代码放到你的项目之前,测量你项目代码性能。

给图层添加阴影时指定阴影路径

让 Core Animation 决定阴影的形状是非常耗时的,这会影响你 app 的性能。你应该使用CALayer 的 shadowPath 属性显式地指定阴影形状,而不是让 Core Animation 决定阴影的形状。你给该属性指定路径对象时,Core Animation 使用该形状绘制、缓存阴影效果。对于图层的阴影形状尽量不要改变,这通过减少 Core Animation 的渲染量极大地提高了性能。

最后