IOS RunLoop检测卡顿原理分析(易懂)
今天主要讲解一下个人对Runloop 应用之-- 检测卡顿的理解。废话不多说直接上图,大神轻喷。
众所周知Runloop是一个循环,那么每一个循环。下面是一张Runloop的流程执行图。展示一个Runloop这个循环在每一个循环之中会做一些什么样子的事情。那么我们一般的事件是分为两种一种是Source0 一种是Source1。这两个Source的作用我已经粘在下面了:
• Source1 :基于mach_Port的,来自系统内核或者其他进程或线程的事件,可以主动唤醒休眠中的RunLoop(iOS里进程间通信开发过程中我们一般不主动使用)。mach_port大家就理解成进程间相互发送消息的一种机制就好, 比如屏幕点击, 网络数据的传输都会触发sourse1。
• Source0 :非基于Port的 处理事件,什么叫非基于Port的呢?就是说你这个消息不是其他进程或者内核直接发送给你的。一般是APP内部的事件, 比如hitTest:withEvent的处理, performSelectors的事件.
我们触摸屏幕,先摸到硬件(屏幕),屏幕表面的事件会被IOKit先包装成Event,通过mach_Port传给正在活跃的APP , Event先告诉source1(mach_port),source1唤醒RunLoop, 然后将事件Event分发给source0,然后由source0来处理。
摘自https://blog.csdn.net/u014600626/article/details/105146577
我们的卡顿当然是针对主线程来说的,所以我们这里只讨论主线程的Runloop。检测的原理也很简单,首先我们要理解卡顿是什么? 卡顿就是不流畅,那为什么不流畅呢? 因为你主线程做了其他的事情占用了CPU和GPU的资源 导致了它们给屏幕渲染的资源就变少了,那么就会导致每一秒可以处理出来的画面不满足60帧,造成丢帧就会感觉卡。 所以说白了检测卡顿就是检测哪一段代码占用主线程的Runloop资源(source)比较多我们找出来给他处理掉或者丢到子线程里面就行了。
那下面开始介绍一下检测卡顿的原理, 因为一个Loop的顺序是如上图所示, 那么在处理完Source1之后会有一个wait的状态变化,我们检测卡顿的原理就是在子线程 开辟一个死循环和一个observer,把这个observer添加到主线程Runloop下的所有Mode里面 目的就是为了检测主线程Runloop的状态变化, 然后通过这个死循环不停的判断observer的状态,判断一次Source1到下一次Source0之间的时间, 这个时候你要自己定义一个时间, 如果发现两次Source之间的时间大于你默认的时间了,那么你就认为出现了导致卡顿的操作,这个时候就需要调用一些底层的函数来获取到当前的程序执行了哪些函数,并且这些函数的堆栈信息都是什么,把他们提取出来进行反编译等操作就可以大致定位到导致卡顿的函数了, 当然你定义的这个时间越精细,定位也越准确 不过可能产生的问题也越多。
关于如何获取堆栈信息看这里https://juejin.cn/post/6910791727670362125讲的很清楚,包括我们的错误日志收集也会用到这里的技术