UITableView的性能优化,面试相关问题

705 阅读5分钟

本文主要从这里个方面讲解UITableView的相关问题:

1,关于UI的数据源绑定问题

2,关于UI的事件传递和事件响应原理

3,关于UI的图像显示原理

4,关于UI的卡顿和掉帧的原因

5,关于UI的绘制原理和异步绘制的实现

6,关于UI的离屏渲染的相关问题

1,UI的数据源绑定

2,UI的事件传递和事件响应原理

2.1,UI的事件响应原理


由此图可以看出,UI的事件的传递流程:

1,UILable,UITextField,UIButton的事件会传递到控件当前的所在的UIView

2,从当前的UIView会找他的父类superView,然后找到UIView所在的UIViewController

3,如果UIView没找到UIViewController,就会传递到UIWindow

4,最终UIViewController或者UIView都会传递到UIWindow,再到UIApplication

5,最后事件被传递到UIApplicationDelegate的方法进行使用

这就是整个事件的传递流程实现,知道了事件传递流程,我们就可以在各个阶段对事件进行处理。

2.2,UI的事件传递

UI的事件响应原理,首先我们需要了解这两个方法的实现:

1,- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; //测试点击的区域


2,- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event; //判断点击是否在区域中

屏幕点击事件的具体流程:

点击屏幕触发点击事件 ---> 触发系统的UIApplication ---> 通过UIApplication找到UIWindow ---> 调用hitTest: withEvent:---> 通过pointInside: withEvent:判断是否在点击区域 ---> 最后找到subviews


通过上图,我们来分析事件的传递:

1,判断当前视图的(alpha > 0.01,ishidden == no, isuserinteractionEnble == true ),可以获取点击事件

2,通过pointInside:withEvent:方法判断是否在点击区域内,如果为Yes执行3

3,通过倒叙遍历视图的subviews,如果有subviews,执行4

4,通过hitTest:withEvent:方法查找superview,找到就传递给superview,实现整个的事件传递。

3,UI的图像显示原理



通过上面两幅图我们可以看出,UI的图像显示需要CPU和GPU共同显示完成。

1,CPU主要负责:对象创建,对象调整,布局计算,文本渲染,图片解码,图像绘制等工作。

2,GPU主要负责:纹理渲染,视图混合,图像形成等工作。

视图UI显示时,UIView会连接到CALayer,CALayer会实现了CADisplayNode方法进行绘制,具体实现见上图详细分析。

4,UI的卡顿和掉帧

1,屏幕显示的时候,是由垂直同步信号(VSync)和水平同步信号(HSync)组成,先发出垂直同步信号,再一行一行的发出水平同步信号进行显示


2,按照每秒60FPS的刷帧率,每隔16.7ms就会有一次VSync信号,如果每一次CPU和CPU工作的总耗时超过16.7ms,就会造成卡顿掉帧


针对UI掉帧和卡顿的优化主要针对CPU和GPU进行优化

CPU:

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

GPU:

  • GPU能处理的最大纹理尺寸是4096x4096,一旦超过这个尺寸,就会占用- CPU资源进行处理,所以纹理尽量不要超过这个尺寸
  • 尽量减少视图数量和层次
  • 减少透明的视图(alpha<1),不透明的就设置opaque为YES
  • 尽量避免出现离屏渲染

参考链接:https://www.jianshu.com/p/a96b7dd7d3ad

5,UI绘制原理和异步绘制

5.1 UI的绘制原理

我们通过两幅图来了解UI的绘制原理实现

1,UIView进行绘制时,UIView会先调用setNeedsDisplay方法

2,然后通过view.layer调用layer的setNeedsDisplay方法

3,传递到CALayer,调用CALayer的display方法

4,CALayer会通过代理回调,去实现displayLayer的方法,如果有就进入异步绘制,否则进入系统绘制流程。否则进入5.2UI的异步绘制

系统绘制流程请看下图


5,CALayer会通过创建backing store的CGContextRef,然后判断layer的delegate

6,如果delegate存在就会调用[layer.delegate drawLayer:inContext:]然后调用UIView的drawRect方法处理视图,传递到GPU

7,如果delegate不存在,就会直接调用CALayer的drawInContext方法,直接处理视图,传递到GPU。

5.2 UI的异步绘制

我们来看一下UI的异步绘制实现流程


 异步绘制主要实现在上图的3.2当中

1,在子线程中,CA Layer的代理负责生成对应的bitMap

2,通过CoreGraphic的API调用实现,把bitMap作为layer的comntent属性值实现

3,最后回到CA Layer的content中。

6,UI的离屏渲染

先谈谈什么会触发离屏渲染?

1,图像设置圆角和maskToBounds一起使用时

2,UI图层设置蒙板

3,UI图层设置阴影

4,GPU实现光栅化操作

避免离屏渲染:优化圆角,少用阴影,渐变

参考:https://www.jianshu.com/p/52c72f18e142

7,面试问题

1,UITableView的滚动更加流畅的思路和方法有哪些?

答:1,提前计算并缓存好高度(布局),因为heightForRowAtIndexPath:是调用最频繁的方法;  

2,滑动时按需加载,防止卡顿,这个我也认为是很有必要做的性能优化,配合SDWebImage 异步绘制,遇到复杂界面,遇到性能瓶颈时,可能就是突破口。

3,异步绘制cell的实现

4, 缓存一切可以缓存的,这个在开发的时候,往往是性能优化最多的方向,缓存view的实现。

5,避免同步现在,使用异步下载网络图片

6,减少渲染的view的层级和数量。