腾讯Bugly特约作者:陈向文
终端的开发,首当其冲的就是视图、动画的渲染,切换等等。用户使用 App 时最直接的体验就是这个界面好不好看,动画炫不炫,滑动流不流畅。UI就是 App 的门面,它的体验伴随着用户使用 App 的整个过程。如果UI失败,用户是不会有打开第二次的欲望的。
iOS 为开发者提供了丰富的 Framework(UIKit,Core Animation,Core Graphic,OpenGL 等等)来满足开发从上层到底层各种各样的需求。不得不说苹果很牛逼,很多接口你根本不需要理解背后的原理就能上手使用并且满足你大部分的需求,但是,如果遇到性能问题就容易抓瞎。易用性跟优化就是个矛盾体,就像 ARC 一样,当你没有遇到内存问题的时候用得很爽,一旦遇到了,就要要求你比在用 MRC 的时候更加了解 iOS 的内存机制。UI 亦是如此。况且,作为鹅厂的员工当然不能仅限于知道怎么用。我们要知其然还要知其所以然。好了,废话不说,我们进入主题:看看 iOS 是如何渲染视图和动画的,以及在我们遇到渲染的性能问题时怎么做优化。
 
(注意:以下内容是笔者的一些踩坑经验和总结, 欢迎探讨!)
先来看看官方对 Core Animation 的一段说明:
可以看出iOS渲染视图的核心是 Core Animation。从底层到上层依此是 GPU->(OpenGL、Core Graphic) -> Core Animation -> UIKit。
在 iOS上,动画和视图的渲染其实是在另外一个进程做的(下面我们叫这个进程 render server),在 iOS 5 以前这个进程叫 SpringBoard,在 iOS 6 之后叫 BackBoard。
下面这幅图是使用项目录制视频的时候(大量视图渲染),整个系统的进程情况:
可以很清楚地看到 BackBoard 这个进程的情况。
 
iOS 上视图或者动画渲染的各个阶段:
在 APP 内部的有4个阶段:
布局:在这个阶段,程序设置 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。
在 APP 外部的2个阶段:
当这些数据到达 render server 后,会被反序列化成 render tree。然后 render server 会做下面的两件事:
  • 根据 layer 的各种属性(如果是动画的,会计算动画 layer 的属性的中间值),用 OpenGL 准备渲染。
  • 渲染这些可视的 layer 到屏幕。
如果做动画的话,最后的两个步骤会一直重复知道动画结束。
我们都知道 iOS 设备的屏幕刷新频率是 60HZ。如果上面的这些步骤在一个刷新周期之内无法做完(1/60s),就会造成掉帧。
我们看看有哪些操作可能会过度消耗 CPU 或者 GPU,从而造成掉帧。
  • 视图上有太多的 layer 或者几何形状:
    如果视图的层级结构太复杂的话,当某些视图被渲染或者 frame 被修改的话,CPU 会花比较多得时间去重新计算 frame。尤其如果用 autolayout 的话,会更消耗 CPU。同时过多的几何结构会大大增多需要渲染的OpenGL 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 或者加载图片会更卡。
  • 离屏的绘制:离屏的绘制有两种情况:1. 有些效果(如 rounded corners,layer masks,drop shadows 和 layer rasterization)不能直接的绘制到屏幕上,必须先绘制到一个 offscreen 的 image context 上,这种操作会引入额外的内存和 CPU 消耗。2. 实现了 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 来看看一些具体的例子。
Core Animation:
Color Blended Layers:看半透明 layer 的遮盖情况。从绿到红,越红遮盖越大。
这下这两幅图是测量项目详情页的半透明的 layer 的情况。可以看到详情页这里半透明的 layer 还是比较多的,但不是说半透明的 layer 很多,范围很大就要优化,要参看 GPU Driver 的测量情况看,下面会介绍
Color Hits Green and Misses Red:当使用shouldRasterize的时候,layer drawing 会被缓存起来,如果 rasterized 的 layer 需要被重新绘制,会标示红。
下面是项目消息列表的 cell 设置 shouldRasterize=YES 的情况,每个 cell 的 layer 的 backing image 会被缓存起来,如果往下滚动 tableview 的话,由于 cell 会被复用,这样 layer 就会被重绘,会被标红。
Color Offscreen-Rendered Yellow:如果要做 offscreen drawing 的话,会标成黄色。
下面是项目的首页,由于圆形头像的实现方式是设置 roundedCorner 的属性来实现,所以会触发 offscreen drawing。
Core Animation Template 只是能让开发者直观地看到哪些地方有可能需要优化,但是到底要不要优化,还是要看 GPU Driver 的表现。
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空间、手机管家相同的质量保障手段

iOS 视图,动画渲染机制探究的更多相关文章

  1. iOS离屏渲染的解释:渲染与cpu、gpu

    重开一个环境(内存.资源.上下文)来完成(部分)图片的绘制 指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作 意为离屏渲染,指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作. ...

  2. iOS系列 基础篇 04 探究视图生命周期

    iOS系列 基础篇 04 探究视图生命周期 视图是应用的一个重要的组成部份,功能的实现与其息息相关,而视图控制器控制着视图,其重要性在整个应用中不言而喻. 以视图的四种状态为基础,我们来系统了解一下视 ...

  3. iOS燃烧动画、3D视图框架、天气动画、立体相册、微信朋友圈小视频等源码

    iOS精选源码 iOS天气动画,包括太阳,云,雨,雷暴,雪动画. 较为美观的多级展开列表 3D立体相册,可以旋转的立方体 一个仪表盘Demo YGDashboardView 一个基于UIScrollV ...

  4. IOS 视图切换动画

    我在网上找到的这个小方法,被我举一反三使用的屡试不爽.比如用在,当视图需要执行某一方法跳转到新的一个UIView上,从底层渐变浮到最上层.就是一个不错的视觉效果或者当需要类似keyboard的效果从底 ...

  5. iOS离屏渲染简书

    更详细地址https://zsisme.gitbooks.io/ios-/content/chapter15/offscreen-rendering.html(包含了核心动画) GPU渲染机制: CP ...

  6. iOS 视图控制器转场详解

    iOS 视图控制器转场详解 前言的前言 唐巧前辈在微信公众号「iOSDevTips」以及其博客上推送了我的文章后,我的 Github 各项指标有了大幅度的增长,多谢唐巧前辈的推荐.有些人问我相关的问题 ...

  7. iOS离屏渲染之优化分析

    在进行iOS的应用开发过程中,有时候会出现卡顿的问题,虽然iOS设备的性能越来越高,但是卡顿的问题还是有可能会出现,而离屏渲染是造成卡顿的原因之一.因此,本文主要分析一下离屏渲染产生的原因及避免的方法 ...

  8. iOS核心动画高级技巧之CALayer(一)

    iOS核心动画高级技巧之CALayer(一) iOS核心动画高级技巧之图层变换和专用图层(二)iOS核心动画高级技巧之核心动画(三)iOS核心动画高级技巧之性能(四)iOS核心动画高级技巧之动画总结( ...

  9. View Programming Guide for iOS ---- iOS 视图编程指南(五)---Animations

      Animations Animations provide fluid visual transitions between different states of your user inter ...

随机推荐

  1. RVM 多版本Ruby管理-Gentoo

    发现了一个非常Amzaing的Ruby的工具RVM,用于安装和管理Ruby的多个版本.相比较于直接在系统中安装不同版本的Ruby,然后使用时切换到对应的版本,这种方式实在是酷毙了,使ruby安装变得非 ...

  2. Caffe入门与应用 by GX

    深度学习几大工具:Theano(基于python),Torch,Caffe(用c++写的),Tensor flow,CNTK:caffe是比较流行的深度学习的框架 caffe特点:特别适合于新手,由于 ...

  3. ctrip

    #-*-coding:utf8-*-from lxml import etreeimport requestsimport re#编码转换import sysreload(sys)sys.setdef ...

  4. hdoj 2066 一个人的旅行

    Problem Description 虽然草儿是个路痴(就是在杭电待了一年多,居然还会在校园里迷路的人,汗~),但是草儿仍然很喜欢旅行,因为在旅途中 会遇见很多人(白马王子,^0^),很多事,还能丰 ...

  5. JavaScript (jquery) 数组去重的算法探讨

                                    方法很巧妙 但是要事先知道对应的name或其他属性名称 主键值只适用于已知数据对象进行调用: var arr1 = [{ name: ' ...

  6. Linux 如何使用压缩与解压缩的方式将Windows下的zip压缩包上传到Linux系统

    当我们无法使用xftp方式上传文件到Linux系统时,我们可以使用在Windows下压缩文件夹,然后到Linux系统下解压缩的方式,完成整个上传工作. 第一步:在Windows系统下,将整个文件夹压缩 ...

  7. 机器学习利器——Scikit-learn的安装

    由于笔者最近在进行毕业论文的准备,且毕业论文中需要用到Python版本的机器学习库——scikit-learn.所以最近三天一直在Windows上部署这个框架,终于部署成功了... 首先打开加州大学底 ...

  8. 让C++程序打印自身源码

    本人原创文章,欢迎阅读,禁止转载. 这绝对是惊艳到让你眼前一亮(为了简洁,故意没考虑资源问题和编译警告). #include <iostream> #include <fstream ...

  9. 关于display: box 和 box-flex

    这两天做手机项目,使用到这个css3新属性.现在还不为所有浏览器支持,所以使用的时候要加上前缀.使用方法见下面: html代码: <div class="s-indLine" ...

  10. Python 4 —— 函数与模块

    函数和模块的使用 一.函数 一个例子说明一切. def hello(): print "hello world" def increment(num): num += 1 retu ...