001性能优化-02-界面优化.md

[TOC]

基础优化方向

  1. 使用 ARC 管理内存

  2. 在正确的地方使用 reuseIdentifier

    • tableview 用 tableView:cellForRowAtIndexPath: 为 rows 分配 cells 的时候,他的数据应该重用自 UITableViewCell
  3. 尽量将 views 设置为不透明(Opaque)

  4. 避免过于庞大的 XIB

  5. 不要阻塞主线程

    • 用于不要是主线程承担过多。因为UIKit在主线程上做所有工作,

    • 渲染、管理触摸反应、回应输入等都需要在主线程上完成

    • 大部分阻碍主线程的情形是你的APP在做一些涉及到读写外部资源的 I/O 操作,比如存储或者网络:

      image
  1. 不要在 viewWillAppear 中做费时的操作

    • viewWillAppear,在 view 显示之前被调用,处于效率考虑,方法中不要处理复杂费时操作。
    • 只能在该方法中设置 view 的显示属性之类的简单事情,例如背景色、字体等。否则,会明显感觉到 view 有卡顿或者延迟。

图片处理

  1. 在 ImageView 中调整图片大小。

  2. 保证图片大小和 UIImageView 大小相同。

    如果要在 UIIImageView 中显示一个来自 bundle 的图片,你应保证图片的大小和 UIImageView 大小相同。

  3. 在运行中缩放图片是耗费资源的,特别是 UIImageView 嵌套在 UIScrollView 中的情况下。

  4. 处理网络图片。如果图片是从远端服务器加载的,你无法控制图片大小。那么就在下载完成之后,最好是在 background thread中,缩放一次,然后在 UIImageView 中使用缩放之后的图片。

  • imageNamed 初始化:加载图片、并缓存

    • imageNamed 默认加载图片成功后,内存中会缓存图片。
    • 这个方法用一个指定的名字在系统缓存中查找并返回一个图片对象。
    • 如果缓存中没有找到响应的图片对象,则从指定地方加载图片,然后缓存对象,并返回这个而图片对象。
  • imageWithContentsOfFile 初始化

    • imageWithContentOfFile 则仅只加载、不缓存。
    • 如果加载一张大图并且使用一次,用 imageWithContentsOfFile 是最好的,这样 CPU 不需要做缓存,可以节约时间。
  • 具体使用哪一种需要根据应用场景加以区分,UIImage 虽小,但是是使用非常多的元素,也会有比较显著的问题。

    • “不以善小而不为,不以恶小而为之”
    • “不积跬步无以至千里”
  • UIImageView

    • 在性能的范围之内,直接对 UIImageView 设置圆角是不会触发离屏渲染的,但同时给 UIImageView 设置背景色肯定会触发离屏渲染
    • 触发离屏渲染跟 .png.jpg 格式无关

UITableView 优化

view相关操作

  • 正确使用 reuseIdentifier 来重用 cells
  • 减少subview的数量
  • 尽量使所有的 view opaque(不透明),包括cell自身
  • 使用 shadowPath 来画阴影
  • 避免渐变、图片缩放

缓存相关操作

  • 缓存行高
  • 尽量不要使用 cellForRowAtIndexPath。如果要使用,只用一次,然后缓存结果
  • 尽量使用 rowHeight、sectionHeight、sectionHeaderHeight 来设定固定的行高,不要请求delegate

数据加载相关操作

  • 如果 cell 内实现的内容来自 web,使用异步加载,缓存请求结果
  • 使用正确的数据结果来存储数据

CPU 和 GPU 层优化

本质上是降低 CPU、GPU的工作,从这两个方面入手去提升性能

  • CPU做了什么事

    • 逻辑运算
    • 对象的创建
    • 对象属性的调整、布局计算、文本的计算和排版
    • 图片的格式转码和解码
    • 图像的绘制
  • GPU做了什么事

    • 数学运算
    • 纹理绘制

CPU 层面的优化

  • 尽量用轻量级的对象,比如用不到时间处理的地方,可以考虑使用 CALayer 取代 UIView
  • 不要频繁调用 UIView 的相关属性,例如 frame、bounds、transform 等属性,尽量减少不必要的修改
  • 尽量提前计算好布局,在有需要时一次性调整对应的属性,不要对此修改属性
  • Autolayout 会比直接设置 frame 消耗更多的 CPU 资源
  • 图片的 size 最好刚好更 UIImageView 的 size 保持一致
  • 控制一下 线程的最大并发数量
  • 尽量把耗时操作放到子线程
    • 文本处理(尺寸计算、绘制)
    • 图片处理(解码、绘制)

GPU 层面的优化

  • 尽量避免短时间内大量图片的显示,尽可能你将多张图片合成一张进行显示
  • GPU 能处理的最大纹理尺寸是 4096x4096,一旦超过这个尺寸,就会占用 CPU 资源进行处理,所以纹理尽量不要超过这个尺寸
  • 尽量减少视图数据和层次
  • 减少透明的视图(alpha<1),不透明的就设置 opaque 为 YES(打开绘制优化开关)
  • 尽量避免出现离屏渲染

离屏渲染优化

如何高性能的画一个圆角?

视图和圆角的大小对帧率并没有什么影响,数量最核心的影响因素

image

这个是我们最常规的设置方式,但不可取,因为会触发离屏渲染

  • 如果能够只用 cornerRadius 解决问题,就不用优化
  • 如果必须设置 maskToBounds,可以参考圆角视图的数量,如果数量少(一页只有几个)也可以考虑不用优化。
  • UIImageView 的圆角通过直接截取图片实现,其他视图的圆角可以通过 Core Graphics 画出圆角矩阵实现
  • 通过 CoreGraphic 画一个圆角,不会触发离屏渲染
image

什么是 “离屏渲染”?

离屏渲染就是在当前屏幕缓冲区外,新开辟一个缓冲区进行操作。

  • 在 OpenGL 中,GPU 有2中渲染方式

    • On-Screen Rendering:当前屏幕渲染,在当前用于显示的屏幕缓冲区进行渲染操作
    • Off-Screen Rendering:离屏渲染,在当前屏幕缓冲区以外开辟一个缓冲区进行渲染操作

为何要避免离屏渲染?

CPU、GPU 在绘制渲染视图是做了大量的工作。离屏渲染发生在 GPU 层面上,会创建新的渲染缓缓冲区,会触发 OpenGL 的多通道渲染管线,图像上下文切换会早晨额外的开销,增加 GPU 工作量。如果CPU、GPU累计耗时 16.67 ms还没有完成,就会造成卡顿掉帧。

圆角属性、蒙层遮罩 都会触发离屏渲染。指定以上属性,标记了它在新的图形上下文中,在未愈合之前,不可以用于显示的时候就触发了离屏渲染。

离屏渲染消耗性能的原因?

  • 需要创建新的缓冲区

  • 离屏渲染的整个过程,需要多次切换上下文环境

    • 上下文先从当前屏幕(On-Screen)切换到离屏(Off-Screen)
    • 等离屏渲染结束后,将离屏缓冲区的结果显示到屏幕上
    • 再将上下文切换到当前屏幕

哪些操作会触发离屏渲染?

  1. layer.shouldRasterzize:光栅化

    • 光栅化概念:将图转化为一个个栅格组成的图像
    • 光栅化特点:每个元素对应帧缓冲区的一像素
    • 光栅化限制:系统给光栅化限制了内存,如果超过就会触发离屏渲染,所以cell中一般不使用
  2. mask:遮罩

  3. shadows:layer.shadowXXX,如果设置了 layer.shadowPath 就不会产生离屏渲染

  4. group opacity:不透明,layer属性

  5. edge antialiasing:抗锯齿

  6. cornerRadius:圆角。同时设置 layer.masksToBounds = YES; layer.cornerRadius 大于0,考虑通过 CoreGraphics 绘制圆角,或者让美工提供圆角图片

  7. 渐变

  8. drawRect

离屏渲染 VS CPU 渲染

上面说到了,所有不在 GPU 的当前屏幕缓冲区进行的渲染都叫离屏渲染,还有另外一种特殊的离屏渲染,叫“CPU 渲染”。

  • CPU渲染:

    • 如果重写了 drawRect 方法,并且使用任何 Core Graphics 的技术,进行了绘制操作,就涉及到了 CPU渲染
    • 整个渲染过程由 CPU 在 APP 内同步完成,渲染得到的 bitmap 最后交给 GPU 用于显示

由于 GPU 的浮点运算能力比 CPU 强,CPU 渲染的效率可能不如离屏渲染。但如果只是实现一个简单的效果,直接使用CPU渲染就可以,因为离屏渲染涉及到缓冲区创建以及上下文切换等耗时操作。

如何检测离屏渲染?

  1. 模拟器:Debug->Color Off-screen Rendered

    image
    image
    image

    可以看到图片亮黄色部分就是离屏渲染的视图

  2. 真机:xcode9之后可以不用 Instrument 了,运行程序之后:Debug -> View Debugging -> Rendering -> Color Offscreen-Rendered Yellow

    设置路径
    image

离屏渲染的解决思路

  1. 预排班,提前计算

    在接收到服务器端返回的数据后,尽量将 CoreText 排版的结果、单个空间的高度、cell 整体的高度提前计算好,将其存储在模型的属性中。需要使用时,知己耳聪模型中往外取,避免了计算的过程。

    尽量少用 UILabel,可以使用 CALayer。避免使用 AutoLayout 的自动布局,才去纯代码的方式

  2. 预渲染,提前绘制

    圆形的图标可以提前在:接收到网络返回数据时,后台线程进行处理,直接存储在模型数据中,回到主线程后直接调用

    避免使用 CALayer 的 Border、Corner、Shadow、Mask 等技术,这些都会触发离屏渲染

  3. 异步绘制

  4. 全局并发线程

  5. 高效的图片异步加载

图层混合优化

  1. 怎么检测图层混合

    在下面 instrument 工具介绍中有给出

  2. 怎么避免图层混合

    • 确保控件的 Opaque 属性设置为 true
    • 确保控件的 背景色 和父视图的背景色一致 且不透明
    • 如无特殊需要,不要设置低于 1 的 alpha 值
    • 确保 UIImage 没有alpha 通道
    • UILabel iOS8以后设置背景色为非透明 和 设置 label.layer.masksToBounds=YES,就可以让label只渲染给定的size区域,解决 UILabel 的图层混合问题

instrument 工具

上面讲到了如何检测离屏渲染,除了离屏渲染 Rendering 还有其他几种调试类型

  • Color Blended Layer :图层混合

    表示区域使用多种混合图层,(图层混合:由于多 UI/Layer 叠加,如果有透明或半透明颜色时,CPU就会去计算最终显示的颜色,中间就涉及很很多多余计算。

    颜色标识

      - 红色:混合图层
      - 绿色:没有使用混合
    

    调优

      减少红色区域
      
      1. 设置 Opaque 属性为 YES
      2. 给 view 设置一个不透明的颜色
    
    image

    image
  • Color Hits green and Misses Red:光栅化(缓存layer)

    检测 layer 是否使用 shouldRasterize,为true开启光栅化(默认),光栅化会将layer预先渲染为位图 bitmap,然后缓存,从而提高性能。

    颜色标识

      - 红色:光栅化
      - 绿色:未光栅化
    

    调优

      使用内容不变的layer,不适合tableview,会造成多余离屏渲染降低性能(原因:系统给光栅化限制了内存,如果超过就会离屏渲染)
    
    image
  • Color copied Images 图片格式检测与复制

    Shows images that are copied by Core Animation in blue,苹果官方注释说 被拷贝给CPU进行转化的图片显示为绿色。

    如果 GPU 不支持当前图片的颜色格式,那么就会将图片交给 CPU 预先进行格式转化,并且这张图片标记为绿色。

    GPU 只解析 32 bit 的颜色格式,如果使用 Color Copied Images 调试,图片是蓝色

    颜色标识

      - 蓝色:需要赋值
    

    扩展

       32bit 指图片颜色深度,用“位”来表示,用来表示显示颜色数量,例如一个图片支持256种颜色,那么就需要256个不同的值来表示不同的颜色,就需要0-255,二进制表示就是从 00000000-11111111,一共需要 8位 二进制数,所以**颜色深度是 8**。通常32bit色彩中使用3个bit表示R(红)G(绿)B(蓝),还有一个 8bit 表示 Alpha(透明度)
    
  • Color misaliged Images:图片尺寸匹配

    目标像素与源像素不对齐的图片,比如图片大小和 UIImageView 大小不一致

    颜色标识

      - 洋红色:图片没有像素对齐
      - 黄色:图片缩放
    

    优化

      尽量匹配大小
    
  • Color Compositing Fast-Path Blue:快速路径

    标记由硬件绘制的路径,显示蓝色,越多越好。可以直接对OpenGL绘制的图像高亮。

    颜色标识

      - 蓝色
    

    优化

      一般不做检测
    
  • Flash updated Regions:重绘区域

    对重绘区域高亮为黄色,会使用 CoreGraphic 绘制,越小越好

    颜色标识

      - 黄色
    
  • Color Immediately:颜色刷新频率

    当执行颜色刷新的时候移除 10ms 的延迟,因为可能在特定情况下你不需要这些延迟,所以使用此选项加快颜色刷新的频率。

    一般用不到

opaque 绘制优化

这个值不是决定视图是否是透明的,而是给绘制系统提供一个个性能优化的开关

  • YES:不透明。UIView 默认为 YES
  • NO:透明。UIButton、UILabel等子类默认为 NO

red + green = yellow

image

在第一篇文章中介绍了一个公式:

R = S + D * (1 - Sa)

- R:像素RGB
- S:源色彩(顶端纹理)
- D:目标颜色(低一层的纹理)
- Sa:源色彩的透明度
  • redView = (1, 0, 0, 1)
  • greenView = (0, 1, 0, 0.5)

R = 1 + 0 * (1-0.5)
G = 0 + 1 * (1-0.5)
B = 0 + 0 * (1-0.5)
A = 1 + 0.5 * (1-0.5)

  • 得出结果是:(1, 0.5, 0, 1)

而当 Sa=1 时,R=S,也就是说。这个时候不管目标颜色是什么,GPU 都不需要做任何计算合成。只需要简单的从这个层进行拷贝,节省了GPU大量的工作量。

你可能感兴趣的:(001性能优化-02-界面优化.md)