卡顿优化

254 阅读3分钟
CPU:

1、尽量用轻量级的对象,比如用不到事件处理的地方,可以考虑使用CALayer取代UIView 2、不要频繁地调用UIView的相关属性,比如frame、bounds、transform等属性,尽量减少不必要的修改 3、尽量提前计算好布局,在有需要时一次性调整对应的属性,不要多次修改属性 4、Autolayout会比直接设置frame消耗更多的CPU资源 5、图片的size最好刚好跟UIImageView的size保持一致 6、控制一下线程的最大并发数量 7、尽量把耗时的操作放到子线程(文本处理(尺寸计算、绘制)、图片处理(解码、绘制)(UIImage->CGImage->解码创建图形上下文context,通过DrawImage绘制到上下文->从context获取解码图片CGImage->包装成UIImage显示))

GPU:

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

离屏渲染:

需要创建新的缓冲区,离屏渲染的整个过程,需要多次切换上下文环境,先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上,又需要将上下文环境从离屏切换到当前屏幕。

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

1、光栅化,layer.shouldRasterize = YES 2、遮罩,layer.mask 3、圆角,同时设置layer.masksToBounds = YES、layer.cornerRadius大于0 4、阴影,layer.shadowXXX(如果设置了layer.shadowPath就不会产生离屏渲染)

卡顿检测:

1、FPS:向主线程的RunLoop的添加一个commonModes的CADisplayLink,每次屏幕刷新的时候都要执行CADisplayLink的方法,所以可以统计1s内屏幕刷新的次数。 2、RunLoop:开辟一个子线程,然后实时计算 kCFRunLoopBeforeSources 和 kCFRunLoopAfterWaiting 两个状态区域之间的耗时是否超过某个阀值,来断定主线程的卡顿情况。 连续5次超时50ms,单次超过250ms认为卡顿,如果CPU 的占用超过了 80%,也捕获函数调用栈认为卡顿。

有两种方式一种是遍历栈帧:

1、遍历栈帧 2、通过Signal获取任意线程调用栈

1801586-9f61a5e26e6ab300.png

1801586-b37ad677cc9acfdb.png

1、创建一个信号量dispatchSemaphore和观察者runLoopObserver,当主线程的runloop状态发生改变时,会调用runLoopObserverCallBack方法,它内部会存储当前的runloop状态。同时,控制信号量用于保证同步操作。 2、将观察者添加到主线程runloop的common模式下观察。 3、其次,将观察者runLoopObserver添加到主线程runloop中观察。 4、创建一个子线程,并开启一个持续的loop(其实就是个while死循环)来监控主线程的runloop状态。 5、当runloop的状态持续为BeforeSources、AfterWaiting两个状态时,说明主线程卡顿,记录当前主线程调用堆栈。

方法耗时监控:

hook objc_msgSend,就掌握所有objc方法的耗时。 1、fishhook,拥有hook的能力 2、通过汇编语言编写出我们的hook_objc_msgSend方法 3、在原有的objc_msgSend方法的调用前后需要加上before_objc_msgSend和after_objc_msgSend方法,以便我们后期的打点计时操作。每当底层调用hook_objc_msgSend方法时,会先调用before_objc_msgSend方法,再调用hook_objc_msgSend方法,最后调用after_objc_msgSend方法。

hook性能优化

用两个stack,一个专门存放LR值;另一个记录函数调用。避免子线程中OC方法的调用记录。pthread_main_np