1.卡顿产生的原因

卡顿对用户来说直观的表现就是界面操作不流畅,比如上下滑动列表不顺畅等。由于UI相关的操作都是在主线程进行的,所以总的来说会产生卡顿就是因为主线程上出现了耗时的操作。

2.卡顿检测方法

2.1.集成相关三方库检测

三方库常用的分析方法有三类。

  • 第一类是监控FPS。
    一般来说,我们约定60FPS即为流畅。那么反过来,如果App在运行期间出现了掉帧,即可认为出现了卡顿。
    监控FPS的方案几乎都是基于CADisplayLink实现的。简单介绍一下CADisplayLink:CADisplayLink是一个和屏幕刷新率保持一致的定时器,一但 CADisplayLink 以特定的模式注册到runloop之后,每当屏幕需要刷新的时候,runloop就会调用CADisplayLink绑定的target上的selector。
    可以通过向RunLoop中添加CADisplayLink,根据其回调来计算出当前画面的帧数。FPS的好处就是直观,小手一划后FPS下降了,说明页面的某处有性能问题。坏处就是只知道这是页面的某处,不能准确定位到具体的堆栈。
  • 第二类是监控RunLoop。
    RunLoop在BeforeSources和AfterWaiting后会进行任务的处理。可以通过信号量阻塞监控线程并设置超时时间,若超时后RunLoop的状态仍为BeforeSources或AfterWaiting,表明此时RunLoop仍然在处理任务,主线程发生了卡顿。
  • 第三类是Ping主线程。
    Ping主线程的核心思想是添加主线程异步任务和自定义时间间隔,在自定义时间间隔过后如果主线程异步里面的任务没有执行,那么可以确认是主线程发生了卡顿。

OC项目常用的检测库有LXDAppFluecyMonitor,是通过检测Runloop实现的。swift项目常用的库有ANREye。是通过Ping主线程实现的。

2.2.使用Time Profiler进行检测

这是instruments自带的工具,但是使用起来会有些卡顿,体验不是很好,建议使用三方库进行检测。

3.卡顿优化方向

  • 尽量用轻量级的对象,比如用不到事件处理的地方,可以考虑使用CALayer取代UIView。
  • 不要频繁地调用UIView的相关属性,比如frame、bounds、transform等属性,尽量减少不必要的修改,因为每次修改都要重新计算和渲染,消耗性能比较多。
  • 尽量提前计算好布局,在有需要时一次性调整对应的属性,不要多次修改属性,因为多次修改也会重新计算和渲染。
  • 图片的size最好刚好跟UIImageView的size保持一致,如果不一致CPU就会对图片进行伸缩操作,这样比较消耗CPU资源。
  • 尽量把耗时的操作放到子线程,这样可以充分利用CPU的多核,这样CPU的资源消耗分担的也比较合理。

4.卡顿优化实践

我们项目是使用的ANREye库进行的检测,在检测过程中发现的最大的问题就是一些数据库相关的操作比较耗时。例如打开数据库,数据库查询等相关操作。分析代码是因为这些操作是直接在主线程进行的,所以造成了卡顿。解决方案就是把数据库操作放在全局异步队列中,然后在主队列中更新UI。

5.总结

一般写代码过程中容易造成卡顿的问题就是主线程存在一些耗时操作,例如数据库操作,图片的处理,大量数据的处理等。在日常编码过程中要养成习惯,将一些耗时操作放在并发队列中异步执行,这样可以避免很多卡顿的问题。