我想各位攻城狮们肯定听过一句话:“过早的优化是万恶之源”。若是你有着丰富的项目经验,一定会对这句话有着自己的体会,而若是编程新手,那么,请牢记这句话。在一个项目开发到后期,优化就会成为一个不可避免的话题,而这时,优化以及性能问题又显得尤为重要。
本文讨论的是iOS的图形性能问题。
影响性能的因素
1、 CALayer的shouldRasterize(光栅化)
这个属性看上去很不好理解,光栅化是将几何数据经过一系列变换后最终转换为像素,从而呈现在显示设备上的过程。光栅化的本质是坐标变换、几何离散化。
把layer的shouldRasterize设为YES后,CALayer会被光栅化为bitmap,layer的阴影等效果也会被保存到bitmap中作为缓存。在使用了shadow或cornerRadius等效果时,缓存使性能得到提升。
但是在使用这个属性前,需要明确3点:
-
更新已经光栅化的CALayer会造成离屏渲染
-
被光栅化的bitmap如果超过100ms没有被使用则会被移除
-
系统限制缓存的大小为2.5 x screen size
2、Offscreen rendering(离屏渲染)
讨论造成离屏渲染的原因之前,先说明什么是离屏渲染:离屏渲染指的是在图像在绘制到当前屏幕前,需要先进行一次渲染,之后才绘制到当前屏幕。在第一次渲染时,GPU(Core Animation)或CPU(Core Graphics)需要额外的一块内存来进行渲染,完成后再绘制到屏幕。offscreen到onscreen需要进行上下文切换,这个切换的性能消耗是昂贵的。
因此,我们必须避免不必要的离屏渲染。
造成离屏渲染的原因有:
- 设置
CALayer
的cornerRadiu
,edgeAntialiasingMask
,allowsEdgeAntialiasing
属性 - 把
CALayer
的maskToBounds
设为YES
- 设置
CALayer
的shadow
属性 - 设置
CALayer
的mask
属性 - 把
CALayer
的allowsGroupOpacity
属性设为YES
而且opacity
小于1
等等…
由此可见,很多常用属性都会造成离屏渲染,在性能要求高的地方,就需要使用另外的实现方案。比如使用shadowPath
代替使用shadow+shadowOffset+shadowColor
;在需要使用圆形图片的tableview
里,使用cornerRadius
设置圆角是下下之选,可以用一张中间为透明圆形的图片进行遮盖来达到圆形的效果,或者在使用前就把图片裁剪为圆形。
3、Blending(混合绘制)
GPU会放弃绘制那些完全被其他图层遮盖的内容。如果两个图层叠加在一起,上面的图层不是完全不透明的,那么GPU便会计算合并两个图层的透明重叠像素,这个过程便是blending
,这同样也是一个消耗资源的过程。
因此,不要随便把一个视图或图层的backgroundColor
设为透明。
使用Instrument进行检查
用Xcode
打开你的项目,选择工具栏上的Product->Profile
,编译成功后会打开Instrument
,在Choose a profile template
页面下选择Core Animation
,进入主界面。(如果需要检测动画帧数,需要使用真机)
点击左上方红色的录制按钮,开始检测:
在页面右下方,有一系列的复选框,利用这几个选项,我们可以很轻松的检查上面所提到的问题:(下面解释摘抄自iOS核心动画高级技巧第十二章)
-
Color Blended Layers - 这个选项基于渲染程度对屏幕中的混合区域进行绿到红的高亮(也就是多个半透明图层的叠加)。由于重绘的原因,混合对GPU性能会有影响,同时也是滑动或者动画帧率下降的罪魁祸首之一。
-
ColorHitsGreenandMissesRed - 当使用
shouldRasterizep
属性的时候,耗时的图层绘制会被缓存,然后当做一个简单的扁平图片呈现。当缓存再生的时候这个选项就用红色对栅格化图层进行了高亮。如果缓存频繁再生的话,就意味着栅格化可能会有负面的性能影响了。 -
Color Copied Images - 有时候寄宿图片的生成意味着
Core Animation
被强制生成一些图片,然后发送到渲染服务器,而不是简单的指向原始指针。这个选项把这些图片渲染成蓝色。复制图片对内存和CPU使用来说都是一项非常昂贵的操作,所以应该尽可能的避免。 -
Color Immediately - 通常
Core Animation Instruments
以每毫秒10次的频率更新图层调试颜色。对某些效果来说,这显然太慢了。这个选项就可以用来设置每帧都更新(可能会影响到渲染性能,而且会导致帧率测量不准,所以不要一直都设置它)。 -
Color Misaligned Images - 这里会高亮那些被缩放或者拉伸以及没有正确对齐到像素边界的图片(也就是非整型坐标)。这些中的大多数通常都会导致图片的不正常缩放,如果把一张大图当缩略图显示,或者不正确地模糊图像,那么这个选项将会帮你识别出问题所在。
-
Color Offscreen-Rendered Yellow - 这里会把那些需要离屏渲染的图层高亮成黄色。这些图层很可能需要用
shadowPath
或者shouldRasterize
来优化。 -
Color OpenGL Fast Path Blue - 这个选项会对任何直接使用OpenGL绘制的图层进行高亮。如果仅仅使用UIKit或者Core Animation的API,那么不会有任何效果。如果使用
GLKView
或者CAEAGLLayer
,那如果不显示蓝色块的话就意味着你正在强制CPU渲染额外的纹理,而不是绘制到屏幕。 -
Flash Updated Regions - 这个选项会对重绘的内容高亮成黄色(也就是任何在软件层面使用
Core Graphics
绘制的图层)。这种绘图的速度很慢。如果频繁发生这种情况的话,这意味着有一个隐藏的bug或者说通过增加缓存或者使用替代方案会有提升性能的空间。
需要注意的重点是这3个:
- Color Blended Layers
勾选后,检查你的应用界面,blended layer
会显示为红色,不透明的为绿色,红色越少越好,如果你的界面一片红海,那就是时候好好优化了。
- ColorHitsGreenandMissesRed
勾选后,如果在你使用了shouldRasterize
的地方界面显示为绿色,则表示使用正确性能良好,如果为红色,则需要考虑优化了。(第一次加载时会显示红色,因为这时还没缓存成功,需要检测重用的过程中(比如tableview
上下滚动)的变化)
- Color Offscreen-Rendered Yellow
如上所述,离屏渲染的地方都标记为黄色。并非所有的黄色区域都是需要优化的,比如UINavigationBar
,因为需要做背景模糊效果,因此它需要离屏渲染。
总结
上述的很多原因分析,希望大家不要有强迫症的感觉,要求自己的所有项目必须按这个标准执行,因为这是不可能的,只是给大家提供一个优化方向,以及出了性能问题以后的分析依据。但是在日常的编码过程中,也要时刻把性能的意识放在心上,写出优秀的代码。