用实战为你讲解UIKit性能调优

在使用UIKit框架的过程中,性能优化是永恒的话题。很多人都看过分析优化滑动性能的文章,但其中不少文章只介绍了优化方法却对背后的原理避而不谈,或者是晦涩难懂而且读者缺乏实践体验的机会。不妨思考一下下面的问题自己是否有一个清晰的认识:


为什么要把控件尽量设置成不透明的,如果是透明的会有什么影响,如何检测这种影响?



为什么cell中的图片,尽可能要使用正确的大小、格式,如果错误会有什么影响,如何检测这种影响?



为什么设置阴影和圆角有可能影响滑动时流畅度?



shouldRasterize和离屏渲染的关系是什么,何时应该使用?


本文会结合Instrument分析影响性能的因素,提出优化方案并解释背后的原理,项目初始demo的下载地址在我的Github,强烈建议每一位读者下载下来随着我一步一步调试、优化。如果觉得对自己有帮助,可以给一个Star表示支持。后面的图片较多,流量党慎入。

基本概念

打开项目后,只要CustomTableCell.swift文件即可,它实现了自定义的UITableViewCell以及内部的UI布局,因为重点在于性能优化,代码实现的就比较随意。

首先按下Command + I打开Instrument,本文主要用到的是Core Animation工具:


打开Core Animation调试

注意这个调试必须使用真机,点击左上角的红色圆圈就会开始录制。新手可能不太熟悉,这里简单介绍一下调试界面:


调试界面

我们需要了解两个两个区域:


这里记录了实时的fps数值,有些地方是0是因为屏幕没有滑动



这是重中之重,接下来我会带大家逐个理解、体验这些调试选项


有过游戏经验的人也许对fps这个概念比较熟悉。我们知道任何屏幕总是有一个刷新率,比如iphone推荐的刷新率是60Hz,也就是说GPU每秒钟刷新屏幕60次,因此两次刷新之间的间隔为16.67ms。这段时间内屏幕内容保持不变,称为一帧()fps表示s per second,也就是每秒钟显示多少帧画面。对于静止不变的内容,我们不需要考虑它的刷新率,但在执行动画或滑动时,fps的值直接反映出滑动的流畅程度。

调试、优化

图层混合

首先我们要明白像素的概念,屏幕上每一个点都是一个像素,像素有RGB三种颜色构成(有时候还带有alpha)。如果某一块区域上覆盖了多个layer,最后的显示效果受到这些layer的共同影响。举个例子,上层是蓝色(RGB=0,0,1),透明度为50%,下层是红色(RGB=1,0,0)。那么最终的显示效果是紫色(RGB=0.5,0,0.5)。这种颜色的混合(blending)需要消耗一定的GPU资源,因为实际上可能不止只有两层。如果只想显示最上层的蓝色,可以把它的透明度设置为100%,这样GPU会忽略下面所有的layer,从而节约了很多不必要的运算。

第一个调试选项"Color Blended Layers"正是用于检测哪里发生了图层混合,并用红色标记出来。因此我们需要尽可能减少看到的红色区域。一旦发现应该想法设法消除它。开始调试后勾选这个选项,我们在手机上可以看到如下的场景:


Color Blended Layers

很多文章里说把控件设置为opaque = true,其原理就是希望避免图层混合,然而这种调优一般情况下用处不大。因为UIViewopaque属性默认值就是true,也就是说只要不是人为设置成透明,都不会出现图层混合。比如demo中就没有任何透明的控件。

对于UIImageView来说,不仅它自身需要是不透明的,它的图片也不能含有alpha通道,这就是为什么图中第三个图片是绿色,而前两个图片是红色的原因。由于本人对PS和图像几乎一窍不通,恕我不能演示如何消除这些图片的红色。我从网上找了一个美女的头像来说明,图像自身的性质可能会对结果有影响,因此如果你确定自己的代码没有问题,而且出现了图层混合,请联系美工或后台解决。

个人认为比opaque属性更重要的是backgroundColor属性,如果不设置这个属性,控件依然被认为是透明的,所以我们做的第一个优化是在CustomTableCell类的init方法中添加一行代码:


1



label.backgroundColor = UIColor.whiteColor()


虽然在白色背景下,这行代码无法肉眼看到效果,但重新调试后我们可以发现label的红色消失了。也正是因为对背景颜色的不重视,它成了影响滑动性能的第一个杀手。

PS:如果label文字有中文,依然会出现图层混合,这是因为此时label多了一个sublayer,如果有好的解决办法欢迎告诉我。

光栅化

光栅化是将一个layer预先渲染成位图(bitmap),然后加入缓存中。如果对于阴影效果这样比较消耗资源的静态内容进行缓存,可以得到一定幅度的性能提升。demo中的这一行代码表示将labellayer光栅化:


1



label.layer.shouldRasterize = true


Instrument中,第二个调试选项是“Color Hits Green and Misses Red”,它表示如果命中缓存则显示为绿色,否则显示为红色,显然绿色越多越好,红色越少越好。勾选这个选项后我们看到如下的场景:


Color Hits Green and Misses Red

光栅化的核心在于缓存的思想。我们自己动手把玩一下,可以发现以下几个有意思的现象:


上下微小幅度滑动时,一直是绿色



上下较大幅度滑动,新出现的label一开始是红色,随后变成绿色



如果静止一秒钟,刚开始滑动时会变红。


这是因为layer进行光栅化后渲染成位图放在缓存中。当屏幕出现滑动时,我们直接从缓存中读取而不必渲染,所以会看到绿色。当新的label出现时,缓存中没有个这个label的位图,所以会变成红色。第三点比较关键,缓存中的对象有效期只有100ms,即如果在0.1s内没有被使用就会自动从缓存中清理出去。这就是为什么停留一会儿再滑动就会看到红色。

光栅化的缓存机制是一把双刃剑,先写入缓存再读取有可能消耗较多的时间。因此光栅化仅适用于较复杂的、静态的效果。通过Instrument的调试发现,这里使用光栅化经常出现未命中缓存的情况,如果没有特殊需要则可以关闭光栅化,所以我们做的第二个优化是注释掉下面这行代码:


1



//    label.layer.shouldRasterize = true


光栅化会导致离屏渲染,这一点待会儿会讲。






原文来自:bestswifter

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值