iOS核心动画高级技巧--(十五)图层性能

在第14章『图像IO』讨论如何高效地载入和显示图像,通过视图来避免可能引起 动画帧率下降的性能问题。在最后一章,我们将着重图层树本身,以发掘最好的性 能。

隐式绘制

寄宿图可以通过Core Graphics直接绘制,也可以直接载入一个图片文件并赋值给contents属性,或事先绘制一个屏幕之外的 CGContext上下文。在之前的两 章中我们讨论了这些场景下的优化。但是除了常见的显式创建寄宿图,你也可以通过以下三种方式创建隐式的:1,使用特性的图层属性。2,特定的视图。3,特定 的图层子类。

了解这个情况为什么发生何时发生是很重要的,它能够让你避免引入不必要的软件绘制行为。

文本

CATextLayerUILabel 都是直接将文本绘制在图层的寄宿图中。事实上这两种方式用了完全不同的渲染方式:在iOS 6及之前, UILabelWebKitHTML渲染引擎来绘制文本,而 CATextLayer 用的是Core Text.后者渲染更迅速,所以 在所有需要绘制大量文本的情形下都优先使用它吧。但是这两种方法都用了软件的 方式绘制,因此他们实际上要比硬件加速合成方式要慢。

不论如何,尽可能地避免改变那些包含文本的视图的frame,因为这样做的话文本就需要重绘。例如,如果你想在图层的角落里显示一段静态的文本,但是这个图 层经常改动,你就应该把文本放在一个子图层中。

光栅化

在第四章『视觉效果』中我们提到了 CALayershouldRasterize 属性,它可以解决重叠透明图层的混合失灵问题。同样在第12章『速度的曲调』中,它也是作为绘制复杂图层树结构的优化方法。
启用 shouldRasterize 属性会将图层绘制到一个屏幕之外的图像。然后这个图 像将会被缓存起来并绘制到实际图层的 contents 和子图层。如果有很多的子图层 或者有复杂的效果应用,这样做就会比重绘所有事务的所有帧划得来得多。但是光 栅化原始图像需要时间,而且还会消耗额外的内存。

当我们使用得当时,光栅化可以提供很大的性能优势(如你在第12章所见),但 是一定要避免作用在内容不断变动的图层上,否则它缓存方面的好处就会消失,而且会让性能变的更糟。

为了检测你是否正确地使用了光栅化方式,用Instrument查看一下Color Hits GreenMisses Red项目,是否已光栅化图像被频繁地刷新(这样就说明图层并不 是光栅化的好选择,或则你无意间触发了不必要的改变导致了重绘行为)。

离屏渲染

当图层属性的混合体被指定为在未预合成之前不能直接在屏幕中绘制时,屏幕外渲染就被唤起了。屏幕外渲染并不意味着软件绘制,但是它意味着图层必须在被显示之前在一个屏幕外上下文中被渲染(不论CPU还是GPU)。图层的以下属性将会 触发屏幕外绘制:

  • 圆角(当和 maskToBounds 一起使用时)
  • 图层蒙板
  • 阴影

屏幕外渲染和我们启用光栅化时相似,除了它并没有像光栅化图层那么消耗大,子图层并没有被影响到,而且结果也没有被缓存,所以不会有长期的内存占用。但是,如果太多图层在屏幕外渲染依然会影响到性能。

有时候我们可以把那些需要屏幕外绘制的图层开启光栅化以作为一个优化方式,前提是这些图层并不会被频繁地重绘。

对于那些需要动画而且要在屏幕外渲染的图层来说,你可以用CAShapeLayercontentsCenter或者shadowPath 来获得同样的表现而且 较少地影响到性能。

CAShapeLayer

cornerRadiusmaskToBounds 独立作用的时候都不会有太大的性能问题, 但是当他俩结合在一起,就触发了屏幕外渲染。有时候你想显示圆角并沿着图层裁 切子图层的时候,你可能会发现你并不需要沿着圆角裁切,这个情况下用 CAShapeLayer 就可以避免这个问题了。

你想要的只是圆角且沿着矩形边界裁切,同时还不希望引起性能问题。其实你可以用现成的UIBezierPath的构造器 + bezierPathWithRoundedRect:cornerRadius:这样做并不会比直接用cornerRadius更快,但是它避免了性能问题。

可伸缩图片

另一个创建圆角矩形的方法就是用一个圆形内容图片并结合第二章『寄宿图』提 到的 contensCenter 属性去创建一个可伸缩图片(见清单15.2).理论上来说,这 个应该比用CAShapeLayer要快,因为一个可拉伸图片只需要18个三角形(一个 图片是由一个3*3网格渲染而成),然而,许多都需要渲染成一个顺滑的曲线。在 实际应用上,二者并没有太大的区别。

使用可伸缩图片的优势在于它可以绘制成任意边框效果而不需要额外的性能消耗。举个例子,可伸缩图片甚至还可以显示出矩形阴影的效果。

shadowPath

在第2章我们有提到 shadowPath 属性。如果图层是一个简单几何图形如矩形或 者圆角矩形(假设不包含任何透明部分或者子图层),创建出一个对应形状的阴影 路径就比较容易,而且Core Animation绘制这个阴影也相当简单,避免了屏幕外的 图层部分的预排版需求。这对性能来说很有帮助。

如果你的图层是一个更复杂的图形,生成正确的阴影路径可能就比较难了,这样子的话你可以考虑用绘图软件预先生成一个阴影背景图。

混合和过度绘制

在第12章有提到,GPU每一帧可以绘制的像素有一个最大限制(就是所谓的fill rate),这个情况下可以轻易地绘制整个屏幕的所有像素。但是如果由于重叠图层的关系需要不停地重绘同一区域的话,掉帧就可能发生了。

GPU会放弃绘制那些完全被其他图层遮挡的像素,但是要计算出一个图层是否被 遮挡也是相当复杂并且会消耗处理器资源。同样,合并不同图层的透明重叠像素 (即混合)消耗的资源也是相当客观的。所以为了加速处理进程,不到必须时刻不 要使用透明图层。任何情况下,你应该这样做:

  • 给视图的 属性设置一个固定的,不透明的颜色
  • 设置opaque 属性为YES

这样做减少了混合行为(因为编译器知道在图层之后的东西都不会对最终的像素 颜色产生影响)并且计算得到了加速,避免了过度绘制行为因为Core Animation可以舍弃所有被完全遮盖住的图层,而不用每个像素都去计算一遍。

如果用到了图像,尽量避免透明除非非常必要。如果图像要显示在一个固定的背景颜色或是固定的背景图之前,你没必要相对前景移动,你只需要预填充背景图片就可以避免运行时混色了。

如果是文本的话,一个白色背景的 UILabel(或者其他颜色)会比透明背景要 更高效。

最后,明智地使用 shouldRasterize属性,可以将一个固定的图层体系折叠成 单张图片,这样就不需要每一帧重新合成了,也就不会有因为子图层之间的混合和过度绘制的性能问题了。

减少图层数量

初始化图层,处理图层,打包通过IPC发给渲染引擎,转化成OpenGL几何图 形,这些是一个图层的大致资源开销。事实上,一次性能够在屏幕上显示的最大图 层数量也是有限的。

确切的限制数量取决于iOS设备,图层类型,图层内容和属性等。但是总得说来 可以容纳上百或上千个,下面我们将演示即使图层本身并没有做什么也会遇到的性 能问题。

裁切

在对图层做任何优化之前,你需要确定你不是在创建一些不可见的图层,图层在以下几种情况下回事不可见的:

  • 图层在屏幕边界之外,或是在父图层边界之外。
  • 完全在一个不透明图层之后。
  • 完全透明

Core Animation非常擅长处理对视觉效果无意义的图层。但是经常性地,你自己 的代码会比Core Animation更早地想知道一个图层是否是有用的。理想状况下,在图层对象在创建之前就想知道,以避免创建和配置不必要图层的额外工作。

对象回收

处理巨大数量的相似视图或图层时还有一个技巧就是回收他们。对象回收在iOS 颇为常见; UITableViewUICollectionView都有用到, MKMapView中的动画pin码也有用到,还有其他很多例子。
对象回收的基础原则就是你需要创建一个相似对象池。当一个对象的指定实例(本例子中指的是图层)结束了使命,你把它添加到对象池中。每次当你需要一个实例时,你就从池中取出一个。当且仅当池中为空时再创建一个新的。

这样做的好处在于避免了不断创建和释放对象(相当消耗资源,因为涉及到内存的分配和销毁)而且也不必给相似实例重复赋值。

Core Graphics绘制

当排除掉对屏幕显示没有任何贡献的图层或者视图之后,长远看来,你可能仍然 需要减少图层的数量。例如,如果你正在使用多个UILabel 或者UIImageView 实例去显示固定内容,你可以把他们全部替换成一个单独的视 图,然后用- drawRect: 方法绘制出那些复杂的视图层级。

这个提议看上去并不合理因为大家都知道软件绘制行为要比GPU合成要慢而且还 需要更多的内存空间,但是在因为图层数量而使得性能受限的情况下,软件绘制很 可能提高性能呢,因为它避免了图层分配和操作问题。

你可以自己实验一下这个情况,它包含了性能和栅格化的权衡,但是意味着你可 以从图层树上去掉子图层(用shouldRasterize,与完全遮挡图层相反)。

-renderInContext: 方法

Core Graphics去绘制一个静态布局有时候会比用层级的 UIView 实例来得快,但是使用UIView实例要简单得多而且比用手写代码写出相同效果要可靠得 多,更边说Interface Builder来得直接明了。为了性能而舍弃这些便利实在是不应 该。

幸好,你不必这样,如果大量的视图或者图层真的关联到了屏幕上将会是一个大问题。没有与图层树相关联的图层不会被送到渲染引擎,也没有性能问题(在他们被创建和配置之后)。

使用 CALayer-renderInContext:方法,你可以将图层及其子图层快照进 一个Core Graphics上下文然后得到一个图片,它可以直接显示在UIImageView中,或者作为另一个图层的contents。不同于shouldRasterize —— 要求图层与图层树相关联 —— ,这个方法没有持续的 性能消耗。

当图层内容改变时,刷新这张图片的机会取决于你(不同于 shouldRasterize,它自动地处理缓存和缓存验证),但是一旦图片被生成, 相比于让Core Animation处理一个复杂的图层树,你节省了相当客观的性能。

总结

本章学习了使用Core Animation图层可能遇到的性能瓶颈,并讨论了如何避免或 减小压力。你学习了如何管理包含上千虚拟图层的场景(事实上只创建了几百 个)。同时也学习了一些有用的技巧,选择性地选取光栅化或者绘制图层内容在合 适的时候重新分配给CPUGPU。这些就是我们要讲的关于Core Animation的全部 了(至少可以等到苹果发明什么新的玩意儿)。

iOS核心动画高级技巧--目录

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容