腾讯 Bugly 特约做者:陈向文缓存
终端的开发,首当其冲的就是视图、动画的渲染,切换等等。用户使用 App 时最直接的体验就是这个界面好很差看,动画炫不炫,滑动流不流畅。UI就是 App 的门面,它的体验伴随着用户使用 App 的整个过程。若是UI失败,用户是不会有打开第二次的欲望的。性能优化
iOS 为开发者提供了丰富的 Framework(UIKit,Core Animation,Core Graphic,OpenGL 等等)来知足开发从上层到底层各类各样的需求。不得不说苹果很牛逼,不少接口你根本不须要理解背后的原理就能上手使用而且知足你大部分的需求,可是,若是遇到性能问题就容易抓瞎。易用性跟优化就是个矛盾体,就像 ARC 同样,当你没有遇到内存问题的时候用得很爽,一旦遇到了,就要要求你比在用 MRC 的时候更加了解 iOS 的内存机制。UI 亦是如此。何况,做为鹅厂的员工固然不能仅限于知道怎么用。咱们要知其然还要知其因此然。好了,废话不说,咱们进入主题:看看 iOS 是如何渲染视图和动画的,以及在咱们遇到渲染的性能问题时怎么作优化。微信
(注意:如下内容是笔者的一些踩坑经验和总结, 欢迎探讨!)网络
先来看看官方对 Core Animation 的一段说明:app
以看出iOS渲染视图的核心是 Core Animation。从底层到上层依此是 GPU->(OpenGL、Core Graphic) -> Core Animation -> UIKit。框架
在 iOS上,动画和视图的渲染实际上是在另一个进程作的(下面咱们叫这个进程 render server),在 iOS 5 之前这个进程叫 SpringBoard,在 iOS 6 以后叫 BackBoard。函数
下面这幅图是使用项目录制视频的时候(大量视图渲染),整个系统的进程状况:布局
能够很清楚地看到 BackBoard 这个进程的状况。性能
布局:在这个阶段,程序设置 View/Layer 的层级信息,设置 layer 的属性,如 frame,background color 等等。优化
建立 backing image:在这个阶段程序会建立 layer 的 backing image,不管是经过 setContents 将一个 image 传給 layer,仍是经过 drawRect:或 drawLayer:inContext:来画出来的。因此 drawRect:等函数是在这个阶段被调用的。
准备:在这个阶段,Core Animation 框架准备要渲染的 layer 的各类属性数据,以及要作的动画的参数,准备传递給 render server。同时在这个阶段也会解压要渲染的 image。(除了用 imageNamed:方法从 bundle 加载的 image 会马上解压以外,其余的好比直接从硬盘读入,或者从网络上下载的 image 不会马上解压,只有在真正要渲染的时候才会解压)。
提交:在这个阶段,Core Animation 打包 layer 的信息以及须要作的动画的参数,经过 IPC(inter-Process Communication)传递給 render server。
当这些数据到达 render server 后,会被反序列化成 render tree。而后 render server 会作下面的两件事:
根据 layer 的各类属性(若是是动画的,会计算动画 layer 的属性的中间值),用 OpenGL 准备渲染。
渲染这些可视的 layer 到屏幕。
若是作动画的话,最后的两个步骤会一直重复知道动画结束。
咱们都知道 iOS 设备的屏幕刷新频率是 60HZ。若是上面的这些步骤在一个刷新周期以内没法作完(1/60s),就会形成掉帧。
咱们看看有哪些操做可能会过分消耗 CPU 或者 GPU,从而形成掉帧。
视图上有太多的 layer 或者几何形状:若是视图的层级结构太复杂的话,当某些视图被渲染或者 frame 被修改的话,CPU 会花比较多得时间去从新计算 frame。尤为若是用 autolayout 的话,会更消耗 CPU。同时过多的几何结构会大大增多须要渲染的 OpenGLl triangles 以及栅格化的操做(将 OpenGL 的 triangles 转化成像素)
太多的 overdraw:overdraw 是指一个像素点被屡次地用颜色填充。这个主要是因为一些半透明的 layer 相互重叠形成的。GPU 的 fill-rate(用颜色填充像素的速率)是有限的。若是 overdraw 太多的话,势必会下降 GPU 的性能。
视图的延后载入:iOS 只有在展现 viewcontroller 的 view 或者访问 viewcontroller 的 view,好比说 someviewcontroller.view 的时候才会加载view。若是在用户点击了某个 button,而且在 button 的响应函数里作了不少消耗 cpu 的工做,这个时候若是 present 某个 viewcontroller 的话,会容易卡顿,尤为是若是 viewcontroller 要从 database 里获取数据,或者从 nib 文件初始化 view 或者加载图片会更卡。
离屏的绘制:离屏的绘制有两种状况:
有些效果(如 rounded corners,layer masks,drop shadows 和 layer rasterization)不能直接的绘制到屏幕上,必须先绘制到一个 offscreen 的 image context 上,这种操做会引入额外的内存和 CPU 消耗。
实现了 drawRect 或者 drawLayer:inContext:,为了支持任意的绘制,core graphic 会建立一个大小跟要画的 view 同样的 backing image。而且当画完的之后要传输到 render server 上渲染。因此没事不要重载 drawRect 等函数却什么都不作。
图片解压:用 imageNamed:从 bundle 里加载会立马解压。通常的状况是在赋值给 UIImageView 的 image 或者 layer 的 contents 或者画到一个 core graphic context 里才会解压。
隐藏的绘制:catextlayer 和 uilabel 都是将 text 画入 backing image 的。若是改了一个包含 text 的 view 的 frame 的话,text 会被从新绘制。
Rasterize:当使用 layer 的 shouldRasterize 的时候(记得设置适当的 laye r的 rasterizationScale),layer 会被强制绘制到一个 offscreen image 上,而且会被缓存起来。这种方法能够用来缓存绘制耗时(好比有比较绚的效果)可是不常常改的 layer,若是 layer 常常变,就不适合用。
离屏绘制: 使用 Rounded corner, layer masks, drop shadows 的效果可使用 stretchable images。好比实现 rounded corner,能够将一个圆形的图片赋值于 layer 的 content 的属性。而且设置好 contentsCenter 和 contentScale 属性。
Blending and Overdraw :若是一个 layer 被另外一个 layer 彻底遮盖,GPU 会作优化不渲染被遮盖的 layer,可是计算一个 layer 是否被另外一个 layer 彻底遮盖是很耗 cpu 的。将几个半透明的 layer 的 color 融合在一块儿也是很消耗的。
咱们要作的:
设置 view 的 backgroundColor 为一个固定的,不透明的 color。
若是一个 view 是不透明的,设置 opaque 属性为 YES。(直接告诉程序这个是不透明的,而不是让程序去计算)
这样会减小 blending 和 overdraw。
若是使用 image 的话,尽可能避免设置 image 的 alpha 为透明的,若是一些效果须要几个图片融合而成,就让设计用一张图画好,不要让程序在运行的时候去动态的融合。
好了,介绍完这些渲染优化须要注意的点,让咱们用 instrument 的 Core Animation 和 GPU driver 来看看一些具体的例子。
Color Blended Layers:看半透明 layer 的遮盖状况。从绿到红,越红遮盖越大。
这下这两幅图是测量项目详情页的半透明的layer的状况。能够看到详情页这里半透明的layer仍是比较多的,但不是说半透明的layer不少,范围很大就要优化,要参看GPU driver的测量状况看,下面会介绍
Color Hits Green and Misses Red:当使用shouldRasterize的时候,layer drawing 会被缓存起来,若是 rasterized 的 layer 须要被从新绘制,会标示红。
下面是全民K歌消息列表的 cell 设置 shouldRasterize=YES 的状况,每一个 cell 的 layer 的 backing image 会被缓存起来,若是往下滚动 tableview 的话,因为 cell 会被复用,这样 layer 就会被重绘,会被标红。
Color Offscreen-Rendered Yellow:若是要作 offscreen drawing 的话,会标成黄色。
下面是项目的首页,因为圆形头像的实现方式是设置 roundedCorner 的属性来实现,因此会触发 offscreen drawing。
Core Animation template 只是能让开发者直观地看到哪些地方有可能须要优化,可是到底要不要优化,仍是要看 GPU driver 的表现。
Renderer Utilization ——若是这个值大于50%的话,表示 GPU 的性能受到 fill-rate 的限制,可能有太多的 Offscreen rendering,overdraw,blending。
Tiler Utilization ——若是这个值大于50%,表示可能有太多的 layers。
咱们以上面的那个项目的详情页为例,看看 GPU driver 的测量:
能够看到这 Renderer Utilization 是20%左右,Tiler Utilization 时15%,能够不用优化,(固然这里咱们先不考虑 CPU 的使用状况,只是单单针对上面 core animation template 的一些测量说不用作针对性的优化)
这里想说的一点是,以上所说的点只是在咱们遇到渲染性能问题的时候給咱们提供优化的方向跟思路。优化每每表明着更复杂,难懂的代码,在没有遇到渲染性能问题的时候不要过分优化。
但愿对你们有帮助!
本文为腾讯Bugly原创文章,如需转载,请标明出处。
想了解更多干货,请搜索关注公众号:腾讯Bulgy,或搜索微信号:weixinBugly,关注咱们
腾讯 Bugly
Bugly 是腾讯内部产品质量监控平台的外发版本,支持iOS和Android两大主流平台,其主要功能是App发布之后,对用户侧发生的crash以及卡顿现象进行监控并上报,让开发同窗能够第一时间了解到app的质量状况,及时修改。目前腾讯内部全部的产品,均在使用其进行线上产品的崩溃监控。腾讯内部团队4年打磨,目前腾讯内部全部的产品都在使用,基本覆盖了中国市场的移动设备以及网络环境,可靠性有保证。使用Bugly,你就使用了和手机QQ、QQ空间、手机管家相同的质量保障手段