一、引起卡顿的原因
页面卡顿有两种情况:掉帧和无法操作UI
1、掉帧
每一帧的形成由两个部分组成,CPU的计算和GPU的渲染,渲染完成后产物就是一帧。如果页面刷新频率是60帧,就是一秒钟60帧,每帧16.7ms,如果CPU和GPU加起来超过这个时间,那么这帧就无法被绘制出来,也就是会掉帧。
2、无法操作UI
主线程中,操作页面和执行代码都是通过runloop来实现的,如果某些时刻执行的代码过多,导致runloop所响应的点击事件无法被及时执行,那么就会有无法操作的现象。
二、卡顿监控
1、掉帧
在开发中,可以实时把当前帧率显示出来,根据数值变化来查看是否有掉帧的现象,这种想法也可以应用到线上,把掉帧时相关信息记录下来。
2、无法操作UI
由于是runloop的问题导致的,所以这里就新建个线程,去对主线程的runloop进行监听,监听的就是kCFRunLoopBeforeSources和kCFRunLoopAfterWaiting这两个状态。如果时间过久,那就认为是卡顿了。
三、卡顿的处理
掉帧:
1、避免使用CPU自定义绘图,无事件处理的地方尽可能的使用CALayer,保持视图的轻量;
2、尽量复用视图,减少视图的添加和移除;例如移除视图需要动画,可使用隐藏属性来实现;
3、避免重写drawRect方法,该方法会开辟额外的内存空间进行CPU绘制,更要避免在其中做耗时操作;
4、在更新布局的时候,减少layoutIfNeeded的使用,尽量只使用setNeedsLayout;
5、将耗时操作放在子线程中进行,减轻主线程的压力
6、避免主线程进行IO相关的操作
7、针对于必须在 CPU 上进行绘制的组件,尝试使用多线程的异步绘制能力,减轻主线程压力
8、图片的大小和UIImageView的size保持一致,避免CPU进行伸缩操作,图片在列表滚动的时候不去加载,列表停止的时候再进行加载,滚动中禁止lottie动画。
9、控制线程的最大并发数量,CPU调度处理也需要耗时,线程过多会使CPU繁忙
10、避免出现离屏渲染,圆角可以用贝塞尔曲线配合CAShapeLayer来画,阴影通过设置贝塞尔曲线也有提升。给imageiView设置圆角不会导致离屏渲染,前提是imageView不要设置背景色。
无法操作UI:
无法操作UI一般就是主线程执行的方法太多,耗时太长了,一般把一些方法尽量放到子线程处理就可以了。这块主要关注的是如何在得知卡顿后,获取到全部线程的堆栈。
1、从内核函数获取当前所有线程。
2、将获取到的线程转成pthread,方便后面操作
3、从pthread可以取到当前线程的上下文,根据上下文可以转成一个DLInfo对象,对象中存储着上一个调用函数的地址,这样就可以获取整个函数调用链。
4、这个时候获取到的函数调用链都是地址,所以下一步要去mach-o中找到符号表,根据地址找到所有的符号,这样就能获取到完整的堆栈了。