当前位置: 首页 > 编程日记 > 正文

iOS进阶之页面性能优化

作者: hi_xgb

地址: http://www.jianshu.com/p/1b5cbf155b31

前言

在软件开发领域里经常能听到这样一句话,“过早的优化是万恶之源”,不要过早优化或者过度优化。我认为在编码过程中时刻注意性能影响是有必要的,但凡事都有个度,不能为了性能耽误了开发进度。在时间紧急的情况下我们往往采用“quick and dirty”的方案来快速出成果,后面再迭代优化,即所谓的敏捷开发。与之相对应的是传统软件开发中的瀑布流开发流程。

卡顿产生的原因


在 iOS 系统中,图像内容展示到屏幕的过程需要 CPU 和 GPU 共同参与。CPU 负责计算显示内容,比如视图的创建、布局计算、图片解码、文本绘制等。随后 CPU 会将计算好的内容提交到 GPU 去,由 GPU 进行变换、合成、渲染。之后 GPU 会把渲染结果提交到帧缓冲区去,等待下一次 VSync 信号到来时显示到屏幕上。由于垂直同步的机制,如果在一个 VSync 时间内,CPU 或者 GPU 没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。这就是界面卡顿的原因。

因此,我们需要平衡 CPU 和 GPU 的负荷避免一方超负荷运算。为了做到这一点,我们首先得了解 CPU 和 GPU 各自负责哪些内容。


上面的图展示了 iOS 系统下各个模块所处的位置,下面我们再具体看一下 CPU 和 GPU 对应了哪些操作。

CPU 消耗型任务

布局计算

布局计算是 iOS 中最为常见的消耗 CPU 资源的地方,如果视图层级关系比较复杂,计算出所有图层的布局信息就会消耗一部分时间。因此我们应该尽量提前计算好布局信息,然后在合适的时机调整对应的属性。还要避免不必要的更新,只在真正发生了布局改变时再更新。

对象创建

对象创建过程伴随着内存分配、属性设置、甚至还有读取文件等操作,比较消耗 CPU 资源。尽量用轻量的对象代替重量的对象,可以对性能有所优化。比如 CALayer 比 UIView 要轻量许多,如果视图元素不需要响应触摸事件,用 CALayer 会更加合适。

通过 Storyboard 创建视图对象还会涉及到文件反序列化操作,其资源消耗会比直接通过代码创建对象要大非常多,在性能敏感的界面里,Storyboard 并不是一个好的技术选择。

对于列表类型的页面,还可以参考 UITableView 的复用机制。每次要初始化 View 对象时先根据 identifier 从缓存池里取,能取到就复用这个 View 对象,取不到再真正执行初始化过程。滑动屏幕时,会将滑出屏幕外的 View 对象根据 identifier 放入缓存池,新进入屏幕可见范围内的 View 又根据前面的规则来决定是否要真正初始化。

Autolayout

Autolayout 是苹果在 iOS6 之后新引入的布局技术,在大多数情况下这一技术都能大大提升开发速度,特别是在需要处理多语言时。比如阿拉伯语下布局是从右往左,通过 Autolayout 设置 leading 和 trailing 即可。

但是 Autolayout 对于复杂视图来说常常会产生严重的性能问题,对于性能敏感的页面建议还是使用手动布局的方式,并控制好刷新频率,做到真正需要调整布局时再重新布局。

文本计算

如果一个界面中包含大量文本(比如微博、微信朋友圈等),文本的宽高计算会占用很大一部分资源,并且不可避免。

一个比较常见的场景是在 UITableView 中,heightForRowAtIndexPath这个方法会被频繁调用,即使不是耗时的计算在调用次数多了之后也会带来性能损耗。这里的优化就是尽量避免每次都重新进行文本的行高计算,可以在获取到 Model 数据后就根据文本内容计算好布局信息,然后将这份布局信息作为一个属性保存到对应的 Model 中,这样在 UITableView 的回调中就可以直接使用 Model 中的属性,减少了文本的计算。

文本渲染


屏幕上能看到的所有文本内容控件,包括 UIWebView,在底层都是通过 CoreText 排版、绘制为 Bitmap 显示的。常见的文本控件 (UILabel、UITextView 等),其排版和绘制都是在主线程进行的,当显示大量文本时,CPU 的压力会非常大。

这一部分的性能优化就需要我们放弃使用系统提供的上层控件转而直接使用 CoreText 进行排版控制。

Wherever possible, try to avoid making changes to the frame of a view that contains text, because it will cause the text to be redrawn. For example, if you need to display a static block of text in the corner of a layer that frequently changes size, put the text in a sublayer instead.

上面这段话引用自 iOS Core Animation: Advanced Techniques,翻译过来的意思就是说包含文本的视图在改变布局时会触发文本的重新渲染,对于静态文本我们应该尽量减少它所在视图的布局修改。

图像的绘制

图像的绘制通常是指用那些以 CG 开头的方法把图像绘制到画布中,然后从画布创建图片并显示的过程。前面的模块图里介绍了 CoreGraphic 是作用在 CPU 之上的,因此调用 CG 开头的方法消耗的是 CPU 资源。我们可以将绘制过程放到后台线程,然后在主线程里将结果设置到 layer 的 contents 中。代码如下:

- (void)display {

dispatch_async(backgroundQueue, ^{

CGContextRef ctx = CGBitmapContextCreate(...);

// draw in context...

CGImageRef img = CGBitmapContextCreateImage(ctx);

CFRelease(ctx);

dispatch_async(mainQueue, ^{

layer.contents = img;

});

});

}


图片的解码

Once an image file has been loaded, it must then be decompressed. This decompression can be a computationally complex task and take considerable time. The decompressed image will also use substantially more memory than the original.

图片被加载后需要解码,图片的解码是一个复杂耗时的过程,并且需要占用比原始图片还多的内存资源。

为了节省内存,iOS 系统会延迟解码过程, 在图片被设置到 layer 的 contents 属性或者设置成 UIImageView 的 image 属性后才会执行解码过程,但是这两个操作都是在主线程进行,还是会带来性能问题。

如果想要提前解码,可以使用 ImageIO 或者提前将图片绘制到 CGContext 中,这部分实践可以参考 iOS Core Animation: Advanced Techniques( http://apprize.info/apple/ios_5/15.html  )

这里多提一点,常用的 UIImage 加载方法有 imageNamed 和 imageWithContentsOfFile。其中 imageNamed 加载图片后会马上解码,并且系统会将解码后的图片缓存起来,但是这个缓存策略是不公开的,我们无法知道图片什么时候会被释放。因此在一些性能敏感的页面,我们还可以用 static 变量 hold 住 imageNamed 加载到的图片避免被释放掉,以空间换时间的方式来提高性能。

GPU消耗型任务

相对于 CPU 来说,GPU 能干的事情比较单一:接收提交的纹理(Texture)和顶点描述(三角形),应用变换(transform)、混合并渲染,然后输出到屏幕上。宽泛的说,大多数 CALayer 的属性都是用 GPU 来绘制。

以下一些操作会降低 GPU 绘制的性能,

大量几何结构

所有的 Bitmap,包括图片、文本、栅格化的内容,最终都要由内存提交到显存,绑定为 GPU Texture。不论是提交到显存的过程,还是 GPU 调整和渲染 Texture 的过程,都要消耗不少 GPU 资源。当在较短时间显示大量图片时(比如 TableView 存在非常多的图片并且快速滑动时),CPU 占用率很低,GPU 占用非常高,界面仍然会掉帧。避免这种情况的方法只能是尽量减少在短时间内大量图片的显示,尽可能将多张图片合成为一张进行显示。

另外当图片过大,超过 GPU 的最大纹理尺寸时,图片需要先由 CPU 进行预处理,这对 CPU 和 GPU 都会带来额外的资源消耗。

视图的混合

当多个视图(或者说 CALayer)重叠在一起显示时,GPU 会首先把他们混合到一起。如果视图结构过于复杂,混合的过程也会消耗很多 GPU 资源。为了减轻这种情况的 GPU 消耗,应用应当尽量减少视图数量和层次,并且减少不必要的透明视图。

离屏渲染

离屏渲染是指图层在被显示之前是在当前屏幕缓冲区以外开辟的一个缓冲区进行渲染操作。

离屏渲染需要多次切换上下文环境:先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上又需要将上下文环境从离屏切换到当前屏幕,而上下文环境的切换是一项高开销的动作。

会造成 offscreen rendering 的原因有:

  • 阴影(UIView.layer.shadowOffset/shadowRadius/…)

  • 圆角(当 UIView.layer.cornerRadius 和 UIView.layer.maskToBounds 一起使用时)

  • 图层蒙板

  • 开启光栅化(shouldRasterize = true)

使用阴影时同时设置 shadowPath 就能避免离屏渲染大大提升性能,后面会有一个 Demo 来演示;圆角触发的离屏渲染可以用 CoreGraphics 将图片处理成圆角来避免。

CALayer 有一个 shouldRasterize 属性,将这个属性设置成 true 后就开启了光栅化。开启光栅化后会将图层绘制到一个屏幕外的图像,然后这个图像将会被缓存起来并绘制到实际图层的 contents 和子图层,对于有很多的子图层或者有复杂的效果应用,这样做就会比重绘所有事务的所有帧来更加高效。但是光栅化原始图像需要时间,而且会消耗额外的内存。

光栅化也会带来一定的性能损耗,是否要开启就要根据实际的使用场景了,图层内容频繁变化时不建议使用。最好还是用 Instruments 比对开启前后的 FPS 来看是否起到了优化效果。

注意:
shouldRasterize = true 时记得同时设置 rasterizationScale

Instruments 使用


Instruments 是一系列工具集,我们这里只演示 Core Animation 的使用。在 Core Animation 选项右下方会看到如下选项,

Color Blended Layers

这个选项选项基于渲染程度对屏幕中的混合区域进行绿到红的高亮显示,越红表示性能越差,会对帧率等指标造成较大的影响。红色通常是由于多个半透明图层叠加引起。

Color Hits Green and Misses Red

当 UIView.layer.shouldRasterize = YES 时,耗时的图片绘制会被缓存,并当做一个简单的扁平图片来呈现。这时候,如果页面的其他区块(比如 UITableViewCell 的复用)使用缓存直接命中,就显示绿色,反之,如果不命中,这时就显示红色。红色越多,性能越差。因为栅格化生成缓存的过程是有开销的,如果缓存能被大量命中和有效使用,则总体上会降低开销,反之则意味着要频繁生成新的缓存,这会让性能问题雪上加霜。

Color Copied Images

对于 GPU 不支持的色彩格式的图片只能由 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,那么不会有任何效果。

Flash Updated Regions

这个选项会把重绘的内容显示为黄色。不该出现的黄色越多,性能越差。通常我们希望只是更新的部分被标记完黄色。

演示

上述几个选项中常用来检测性能的是 Color Blended Layers、Offscreen-Rendered Yellow 和 Color Hits Green and Misses Red。下面我重点演示一下离屏渲染和光栅化的检测,写了一个简单的 Demo 设置了阴影效果,代码如下:


view.layer.shadowOffset = CGSizeMake(1, 1);

view.layer.shadowOpacity = 1.0;

view.layer.shadowRadius = 2.0;

view.layer.shadowColor = [UIColor blackColor].CGColor;

//    view.layer.shadowPath = CGPathCreateWithRect(CGRectMake(0, 0, 50, 50), NULL);


shadowPath 没有设置时用 Instruments 检测 FPS 基本在 20 以下(iPhone6设备),设置了 shadowPath 后基本维持在 55 左右,性能提升十分明显。

下面来看一下光栅化的检测,代码如下,


view.layer.shouldRasterize = YES;

view.layer.rasterizationScale = [UIScreen mainScreen].scale;


勾选 Color Hits Green and Misses Red 选项后显示如下:



我们可以看到在静止时缓存都生效了,在快速滑动时缓存基本不起作用,因此是否要开启光栅化还是得根据具体场景,用 Instruments 检测开启前后的性能来决定。

总结

本文主要总结了性能调优的一些理论知识,后面还介绍了 Instruments 中 Core Animation 的一些性能检测指标用法。性能优化最重要的是要使用工具来检测而不是猜测,先查看是否有离屏渲染等问题,再用 Time Profiler 分析一下耗时的函数调用。修改后再用工具分析是否有改善,一步一步执行,小心仔细。

建议大家也实际动手分析一下自己的应用,加深一下印象,enjoy~

参考资料

  • http://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/

  • http://www.samirchen.com/use-instruments/

  • http://apprize.info/apple/ios_5/13.html


相关文章:

LabelMe图像数据集下载

Download MATLAB Toolbox for the LabelMe Image Database 利用Matlab Toolbox工具箱下载图像库 一、下载Matlab Toolbox工具箱 1. Github repository We maintain the latest version of the toolbox on github. To pull the latest version, make sure that "git" …

win8计算机管理没有用户组,Win8右键计算机管理提示“该文件没有与之关联的程序”怎么办?...

最近有Win8用户反映,右键计算机管理的时候,出现提示“该文件没有与之关联的程序来执行该操作”,这让用户非常苦恼。那么,Win8右键计算机管理提示“该文件没有与之关联的程序”怎么办呢?下面,我们就一起往下…

Objective-C 自动生成文档工具:appledoc

来源:iOS_小松哥 www.jianshu.com/p/fd4d8d6b6177 如有好文章投稿,请点击 → 这里了解详情 由于最近琐事比较多,所以好久没有写文章了。今天我们聊一聊Objective-C自动生成文档。 做项目的人多了,就需要文档了。手工写文档是一件…

linux命令--提升

查看系统进程:top 查看磁盘空间: df -h 查询系统负载: uptime , 以下显示输入uptime的信息: 04:03:58 up 10 days, 13:19, 1 user, load average: 0.54, 0.40, 0.20 1.当前时间 04:03:58 2.系统已运行的时间 10 days, 13:19 3.前在线用户…

git 从远程主服务器当中创建新分支

现有版本; h20, h28,h26,i8 h28,h26,i8是从H20下面创建的。 需求: 从H28下面创建新分支继续开发。 思路: 所有代码均是放置到H20上仓库当中,首先下载H20完整仓库,也就是.git文件夹当中内容,其本质是一个ZIP…

涉密计算机用户账号设置审批表,北京邮电大学涉密计算机配置审批表.PDF

北京邮电大学涉密计算机配置审批表北京邮电大学涉密计算机配置审批表使用部门 品牌型号涉密计算机类型 ?台式机 ?便携机 资产编号用途 ?科研 ?办公 ?其他 配置日期硬盘序列号中央处理器硬盘容量CPU基本配置 内 存显示器品牌型号MAC 地址操作系统版本 操作系统安装时间放置…

Oracle 正则表达式

ORACLE中的支持正则表达式的函数主要有下面四个:1,REGEXP_LIKE :与LIKE的功能相似2,REGEXP_INSTR :与INSTR的功能相似3,REGEXP_SUBSTR :与SUBSTR的功能相似4,REGEXP_REPLACE &#x…

制作 Swift 和 Objective-C Mixed 的 Pod

来源:南栀倾寒 www.jianshu.com/p/c7623c31d77b 如有好文章投稿,请点击 → 这里了解详情 知识背景 What is CocoaPods(https://guides.cocoapods.org/using/getting-started.html) What did CocoaPods do?&#x…

SearchRequestBuilder常用方法说明

SearchRequestBuilder常用方法说明 (1) setIndices(String... indices):上文中描述过,参数可为一个或多个字符串,表示要进行检索的index;(2) setTypes(String... types):参数可为一个或多个字符串,表示要进…

计算机知识课后反思,计算机硬件和软件知识课后反思

计算机硬件和软件知识课后反思《计算机系统组成》—计算机硬件和软件知识一课是七年级信息技术中《信息技术基础》里的知识。在学习这之前,学生虽然都使用过计算机,但对于计算机的系统组成、主机内的硬件知识基本知之甚少。但是对这些知识学生又充满了好…

iOS超全开源框架、项目和学习资料汇总:UI篇

2017-01-30 iOS巍 CocoaChina原文 上下拉刷新控件 1. MJRefresh --仅需一行代码就可以为UITableView或者CollectionView加上下拉刷新或者上拉刷新功能。可以自定义上下拉刷新的文字说明。(推荐) 2. SVPullToRefresh --下拉刷新控件4500star,…

NYOJ 90 —— 求正整数n划分为若干个正整数的划分个数

整数划分 时间限制:3000 ms | 内存限制:65535 KB描述将正整数n表示成一系列正整数之和:nn1n2…nk, 其中n1≥n2≥…≥nk≥1,k≥1。 正整数n的这种表示称为正整数n的划分。求正整数n的不 同划分个数。 例如正整数6有如…

调整命令行的列数和行数 mode con: cols=100 lines=10000

mode con: cols100 lines10000转载于:https://www.cnblogs.com/passer1991/archive/2013/03/25/2980285.html

写一个 iOS 复杂表单的正确姿势

前言 这几天项目的新需求中有个复杂的表单界面,在做的过程中发现要比想象中复杂很多,有好多问题需要处理。有很多东西值得写下来好好梳理下。 需求分析: 6创建网店1.png上图便是UI根据需求给的高保真, 我们先根据这张图片来描述一…

2014计算机三级网络技术,2014计算机三级网络技术综合题解题思路

2014计算机三级网络技术综合题解题思路,全部自码第一小题 IP地址的计算公式正常IP地址计算:已知IP地址;子网掩码;地址类别:A类地址:1—126(00)B类地址:128—191(10)C类地址:192—223(110) D类地…

word 生成HTML

View Code 1 string wordPath Server.MapPath("/Fileword/" FileUpload1.FileName); 2 string htmlPath Server.MapPath("/Fileword/测试.html");3 //上传word文件4 FileUpload1.SaveAs(wordP…

CCF系列之画图(201409-2)

试题编号: 201409-2试题名称: 画图时间限制: 1.0s内存限制: 256.0MB问题描述: 问题描述在一个定义了直角坐标系的纸上,画一个(x1,y1)到(x2,y2)的矩形指将横坐标范围从x1到x2,纵坐标范围从y1到y2…

大连理工计算机专业导师,大连理工大学计算机科学与技术学院研究生导师简介-申彦明...

大连理工大学计算机科学与技术学院研究生导师简介-申彦明大连理工大学 免费考研网/2016-05-04申彦明院系:计算机科学与技术学院办公电话:无电子信箱:shendlut.edu.cn更新时间:2014-4-4其他专业:计算机系统结构个人简介…

iOS基础问答面试题连载-附答案

2017-02-02 timhbw CocoaChina以下是一些自己收集的比较基础的问题(大神可以忽略),附上答案,方便大家阅读。俗话说得好,基础不牢,地动山摇。文章末尾会提供PDF版的文档,方便大家木有网的时候也可…

一个非常简单的 ASP.NET MVC 示例:长轮询(又叫:反向 AJAX,英文名:Comet)实现...

关于 长轮询&#xff08;又叫&#xff1a;反向 AJAX&#xff0c;英文名&#xff1a;Comet&#xff09;的介绍&#xff0c;请查看&#xff1a;反向Ajax&#xff0c;第1部分&#xff1a;Comet介绍 下面是代码实现&#xff1a; UI: <p><input type"button" onc…

周记 2016.4.5

1. BUILD_IDpleaseDontKillMe /usr/local/tomcat_car/bin/shutdown.sh /usr/local/tomcat_car/bin/startup.sh 2. windown中mysql解压版设置密码: 最开始mysql没有密码&#xff0c;启动mysql后&#xff1a; cmd -- mysql -u root ----- use mysql , 然后执行下面命令 update…

西北工业大学21计算机考研,西北工业大学2018年计算机考研879专业综合考试大纲...

题号&#xff1a;879《专业综合》考试大纲《专业综合》含数据结构、计算机网络、计算机组成原理、信号与系统四部分组成&#xff0c;四选二。一、数据结构1. 数据结构、抽象数据类型的概念;2. 线性结构的相关内容。通用线性表和特殊线性表(栈、队列、广义表等)的逻辑结构以及物…

iOS 实现多个可变 cell 复杂界面的制作

来源&#xff1a;飘游人 www.jianshu.com/p/9fc838d46f5e 如有好文章投稿&#xff0c;请点击 → 这里了解详情 在日常的开发中&#xff0c;有时会遇到内容块比较多&#xff0c;且又可变的界面&#xff1a; 这个界面中有些内容块是固定出现的&#xff0c;比如最上面的商品详情图…

做销售如何跟单,逼单!共20招!(转)

逼单是整个销售业务过程中最重要的一个环节。如果逼单失败你的整个业务就会失败&#xff0c;其实整个业务过程就是一个“逼”的过程&#xff0c;逼要掌握技巧&#xff0c;不要太操之过急&#xff0c;也不要慢条斯理&#xff0c;应该张弛有度&#xff0c;步步为营&#xff0c;也…

how to write Makefile

http://www.cnblogs.com/aoyihuashao/archive/2010/01/18/1650865.html转载于:https://www.cnblogs.com/hSheng/archive/2013/03/27/2985145.html

职称计算机word2007难吗,职称计算机word2007原题

全国职称计算机考试题库——word2007模块word2007模块第1套试题1.修改当前文本窗体类型为"数字",默认数字为"5"。2.设置用户在键入“中兴瑞典”时自动替换为“中星睿典”。3.将当前图片位置改为四周型环绕。4.用WORD的插入技术,插入如下字符&#xff1a;④…

iOS 9 通用链接(Universal Links)

来源&#xff1a;iOS_小松哥 www.jianshu.com/p/734c3eff8feb 如有好文章投稿&#xff0c;请点击 → 这里了解详情 什么是Universal Links? 在iOS9之前&#xff0c;对于从各种从浏览器、Safari中唤醒APP的需求&#xff0c;我们通常只能使用scheme。但是这种方式需要提前判断系…

为什么你的工作经验不值钱

前言 每年的三月到六月&#xff0c;都是招聘高峰&#xff0c;除了大量的应届毕业生涌入社会之外&#xff0c;还有一些工作了一两年尚未找到稳定归属感的人&#xff0c;也会开始投递简历&#xff0c;是的&#xff0c;基本都是在拿了年终奖之后。作为前端技术主管&#xff0c;有幸…

http://bbs.phome.net/showthread-13-45519-0.html

适用于想使用EmpireCMS建站的用户&#xff1b;EmpireCMS零基础的用户&#xff1b;会使用Dreamweaver设计简单表格式模板的用户。教程在线观看&#xff1a; 安装实例教程&#xff1a; http://www.phome.net/doc/ecmsedu/rm/install.htm入门实例教程&#xff1a; http://www.phom…

计算机32位操作系统最大识别到内存,win7 32位系统可以支持多大的内存_win7 的32位系统最大支持多少g的内存...

大家都知道win7系统有32位和64位之分&#xff0c;而两者的安装配置是不一样的&#xff0c;一般4G内存的我们安装64位的系统&#xff0c;但是很多用户不知道win7 32位系统可以支持多大的内存&#xff0c;这就给大家分享一下win7 的32位系统最大支持多少g的内存吧。一、内存和操作…