欢迎加入QQ讨论群258996829
麦子学院 头像
苹果6袋
6
麦子学院

UIKit如何进行性能调优(续)

发布时间:2016-06-16 23:27  回复:0  查看:2786   最后回复:2016-06-16 23:27  

上一篇我们说过,在使用 UIKit框架 的过程中,性能优化是永恒的话题 也为大家讲了部分 UIKit性能调优 的知识,这篇文章将继续为大家用实战详细讲解。
颜色格式
像素在内存中的布局和它在磁盘中的存储方式并不相同。考虑一种简单的情况:每个像素有 R G B alpha 四个值,每个值占用 1 字节,因此每个像素占用 4 字节的内存空间。一张 1920*1080 的照片 (iPhone6 Plus 的分辨率 ) 一共有 2,073,600 个像素,因此占用了超过 8Mb 的内存。但是一张同样分辨率的 PNG 格式或 JPEG 格式的图片一般情况下不会有这么大。这是因为 JPEG 将像素数据进行了一种非常复杂且可逆的转化。
当我们打开 JPEG 格式的图片时, CPU 会进行一系列运算,将 JPEG 图片解压成像素数据。显然这个工作会消耗不少时间,所以不应该在滑动时进行,我们应该预先处理好图片。借用 WWDC 上的一页 PPT 来说明:
UIKit如何进行性能调优(续)
显示流程
Commit Transaction Decode 在同一帧内进行,如果这两个操作的耗时超过 16.67s Draw Calls 就会延迟到下一帧,从而导致 fps 值的降低。下面是 Commit Transaction 的详细流程:
UIKit如何进行性能调优(续)
解码与转换
在第三步的 Prepare 中, CPU 主要处理两件事:
把图片从 PNG JPEG 等格式中解压出来,得到像素数据。
如果 GPU 不支持这种颜色各式, CPU 需要进行格式转换。
比如应用中有一些从网络下载的图片,而 GPU 恰好不支持这个格式,这就需要 CPU 预先进行格式转化。第三个选项 “Color Copied Images” 就用来检测这种实时的格式转化,如果有则会将图片标记为蓝色。
遗憾的是由于我对图片格式不太了解,也不会使用相关工具,并没有能模拟出触发这个选项的场景。我们要记住的是,如果调试时发现有图片被标记为蓝色,说明图片格式出现了一些问题。
图片大小
第四个选项的使用场景不多,我们直接看一下第五个选项 “Color Misaligned Images” 。它表示如果图片需要缩放则标记为**,如果没有像素对齐则标记为紫色。勾选上这个选项并进行调试,可以看到如下场景:
UIKit如何进行性能调优(续) 
图片缩放
demo 中,每个 UIImageView 的大小都是 180x180 ,而只有第二张图片的像素大小是 360x360 。因此除了第二张图片,其他的图片都需要被缩放。图片的缩放需要占用时间,因此我们要尽可能保证无论是本地图片还是从网络或取得图片的大小,都与其 frame 保持一致。
第三个优化是调整所有图片的像素大小以避免不必要的缩放。
离屏渲染
离屏渲染表示渲染发生在屏幕之外,你可能认为这是一句废话。为了真正解释清楚什么是离屏渲染,我们先来看一下正常的渲染通道 (Render-Pass)
UIKit如何进行性能调优(续)
正常渲染通道
首先, OpenGL 提交一个命令到 Command Buffer ,随后 GPU 开始渲染,渲染结果放到 Render Buffer 中,这是正常的渲染流程。但是有一些复杂的效果无法直接渲染出结果,它需要分步渲染最后再组合起来,比如添加一个蒙版 (mask)
UIKit如何进行性能调优(续)
离屏渲染
在前两个渲染通道中, GPU 分别得到了纹理 (texture ,也就是那个相机图标 ) layer( 蓝色的蒙版 ) 的渲染结果。但这两个渲染结果没有直接放入 Render Buffer 中,也就表示这是离屏渲染。直到第三个渲染通道,才把两者组合起来放入 Render Buffer 中。离屏渲染意味着把渲染结果临时保存,等用到时再取出,因此相对于普通渲染更占用资源。
第六个选项 “Color Offscreen-Rendered Yellow” 会把需要离屏渲染的地方标记为**,大部分情况下我们需要尽可能避免**的出现。离屏渲染可能会自动触发,也可以手动触发。以下情况可能会导致触发离屏渲染:
重写 drawRect 方法
mask 或者是阴影 (layer.masksToBounds, layer.shadow*) ,模糊效果也是一种 mask
layer.shouldRasterize = true
前两者会自动触发离屏渲染,第三种方法是手动开启离屏渲染。
开始调试并勾选 “Color Offscreen-Rendered Yellow” ,会看到这样的场景:
UIKit如何进行性能调优(续) 
离屏渲染
如果没有进行第二步优化,你会发现 label 也是**。可以看到 tabbar statusBar 也是**,这是因为它们使用了模糊效果。图片也是**,这说明它也进行了离屏渲染,观察源码后发现主要原因是它使用了阴影,接下来我们进行第四个优化,在设置阴影效果的四行代码下面添加一行:
1
imgView.layer.shadowPath = UIBezierPath(rect: imgView.bounds).CGPath
这行代码制定了阴影路径,如果没有手动指定, Core Animation 会去自动计算,这就会触发离屏渲染。如果人为指定了阴影路径,就可以免去计算,从而避免产生离屏渲染。
设置 cornerRadius 本身并不会导致离屏渲染,但很多时候它还需要配合 layer.masksToBounds = true 使用。根据之前的总结,设置 masksToBounds 会导致离屏渲染。解决方案是尽可能在滑动时避免设置圆角,如果必须设置圆角,可以使用光栅化技术将圆角缓存起来:
1
2
3
4
5
//  设置圆角
label.layer.masksToBounds = true
label.layer.cornerRadius = 8
label.layer.shouldRasterize = true
label.layer.rasterizationScale = layer.contentsScale
快速路径
还记得之前将离屏渲染和渲染路径时的示意图么,离屏渲染的最后一步是把此前的多个路径组合起来。如果这个组合过程能由 CPU 完成,就会大量减少 GPU 的工作。这种技术在绘制地图中可能用到。
第七个选项 “Color Compositing Fast-Path Blue” 用于标记由硬件绘制的路径,蓝色越多越好。
变化区域
刷新视图时,我们应该把需要重绘的区域尽可能缩小。对于未发生变化的内容则不应该重绘,第八个选项 “Flash updated Regions” 用于标记发生重绘的区域。一个典型的例子是系统的时钟应用,绝大多数时候只有显示秒针的区域需要重绘:
UIKit如何进行性能调优(续) 
重绘区域
原文来自: bestswifter

您还未登录,请先登录

热门帖子

最新帖子