iOS之性能优化面试题6

134 阅读4分钟

这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战

如何检测离屏渲染?

  1. 模拟器 debug-选中 color Offscreen - Renderd 离屏渲染的图层高亮成黄 可能存在性能问题
  2. 真机 Instrument-选中 Core Animation-勾选 Color Offscreen-Rendered Yellow 离屏渲染的触发方式

设置了以下属性时,都会触发离屏绘制:

  1. layer.shouldRasterize(光栅化)

    光栅化概念:将图转化为一个个栅格组成的图象。
    光栅化特点:每个元素对应帧缓冲区中的一像素。

  2. masks(遮罩)

  3. shadows(阴影)

  4. edge antialiasing(抗锯齿) 

  5. group opacity(不透明)

  6. 复杂形状设置圆角等 

  7. 渐变

  8. drawRect

例如我们日程经常打交道的 TableViewCell,因为 TableViewCell 的重绘是很频繁的(因为 Cell 的复用),如 果 Cell 的内容不断变化,则 Cell 需要不断重绘,如果此时设置了 cell.layer 可光栅化。则会造成大量的离屏渲 染,降低图形性能。

如果将不在 GPU 的当前屏幕缓冲区中进行的渲染都称为离屏渲染,那么就还有另一种特殊的“离屏渲染” 方式:CPU 渲染。如果我们重写了 drawRect 方法,并且使用任何 Core Graphics 的技术进行了绘制操作, 就涉及到了 CPU 渲染。整个渲染过程由 CPU 在 App 内同步地完成,渲染得到的 bitmap 最后再交由 GPU 用于显示。

现在摆在我们面前得有三个选择:当前屏幕渲染、离屏渲染、CPU 渲染,该用哪个呢?这需要根据具体的 使用场景来决定。

尽量使用当前屏幕渲染

鉴于离屏渲染、CPU 渲染可能带来的性能问题,一般情况下,我们要尽量使用当前屏幕渲染。

离屏渲染 VS CPU 渲染

由于 GPU 的浮点运算能力比 CPU 强,CPU 渲染的效率可能不如离屏渲染;但如果仅仅是实现一个简单的效果,直接使用 CPU 渲染的效率又可能比离屏渲染好,毕竟离屏渲染要涉及到缓冲区创建和上下文切换等 耗时操作

UIButton 的 masksToBounds = YES 又设置 setImage、setBackgroundImage、[button setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:@"btn_selected"]]];

下发生离屏渲染,但是[button setBackgroundColor:[UIColor redColor]];是不会出现离屏渲染的

关于 UIImageView,现在测试发现(现版本:iOS10),在性能的范围之内,给UIImageView设置圆角是不会触发 离屏渲染的,但是同时给 UIImageView 设置背景色则肯定会触发.触发离屏渲染跟 png.jpg 格式并无关联。

日常我们使用 layer 的两个属性,实现圆角 imageView.layer.cornerRaidus = CGFloat(10); imageView.layer.masksToBounds = YES;

这样处理的渲染机制是 GPU 在当前屏幕缓冲区外新开辟一个渲染缓冲区进行工作,也就是离屏渲染,这会 给我们带来额外的性能损耗。如果这样的圆角操作达到一定数量,会触发缓冲区的频繁合并和上下文的的 频繁切换,性能的代价会宏观地表现在用户体验上——掉帧。

怎么检测图层混合?

  1. 模拟器 debug- 选中 color blended layers 红色区域表示图层发生了混合
  2. Instrument-选中 Core Animation-勾选 Color Blended Layers

避免图层混合:

  1. 确保控件的 opaque 属性设置为 true,确保 backgroundColor 和父视图颜色一致且不透明
  2. 如无特殊需要,不要设置低于 1 的 alpha 值
  3. 确保 UIImage 没有 alpha 通道

UILabel 图层混合解决方法:

iOS8 以后设置背景色为非透明色并设置label.layer.masksToBounds=YES 让 label 只会渲染她的实际 size 区域,就能解决 UILabel 的图层混合问题

iOS8 之前只要设置背景色为非透明的就行

为什么设置了背景色但是在 iOS8 上仍然出现了图层混合呢?

UILabel 在 iOS8 前后的变化,在 iOS8 以前,UILabel 使用的是 CALayer 作为底图层,而在 iOS8 开始, UILabel 的底图层变成了_UILabelLayer,绘制文本也有所改变。在背景色的四周多了一圈透明的边,而这 一圈透明的边明显超出了图层的矩形区域,设置图层的 masksToBounds 为 YES 时,图层将会沿着 Bounds 进行裁剪 图层混合问题解决了