iOS App使用GCD导致的卡顿现象及解决方法

2020-01-21 06:33:45丽君

最近在调研 iOS app 中存在的各种卡顿现象以及解决方法。

iOS App 出现卡顿(stall)的概率可能超出大部分人的想象,尤其是对于大公司旗舰型 App。一方面是由于业务功能不停累积,各个产品团队之间缺乏协调,大家都忙着增加功能,系统资源出现瓶颈。另一方面的原因是老设备更新换代太慢,iOS 设备的耐用度极好,现在还有不少 iPhone 4S 在服役,iPhone 6 作为问题设备持有量很高,据估计,现在 iPhone 6s 以前的设备占有比高达 40%。

所以,如果尝试在线上 App 加入卡顿检测的工具,你会发现卡顿出现的概率高的惊人。但卡顿的检测就修复并不简单,主要是因为难以在开发设备上复现。

之前写过一篇介绍主线程卡顿监控的文章,好像现在主流的做法都是通过监控 Runloop 事件回调,检查进入回调的时间间隔是否超过 Threshold,超过则记录当前 App 所有线程的 call stack。

我前段时间从后台上报的卡顿日志里看到这样一个 call stack:

> 0 libsystem_kernel.dylib __workq_kernreturn
> 1 libsystem_pthread.dylib _pthread_workqueue_addthreads
> 2 libdispatch.dylib _dispatch_queue_wakeup_global_slow
> 3 libdispatch.dylib _dispatch_queue_wakeup_with_qos_slow
> 4 libdispatch.dylib dispatch_async

也就是说卡顿出现在 dispatch_async,以我现有对于 GCD 的认知,dispatch_async 是绝无可能出现卡顿的。dispatch_async 的主要任务是从系统线程池里取出一个工作线程,并将 block 放到该线程里去执行。

上述 call stack 确确实实的出现了,而且样本数量还不少,最后一个函数明显是一个内核调用。从函数名字猜测,可能是 GCD 尝试从线程池里获取线程,但已有线程都在执行状态,所以向系统内核申请创建新的线程。但创建线程的内核调用会很慢吗?会慢到让主线程出现卡顿的程度?带着疑问我搜索了大量相关资料,最后比较相关的有这样一篇文章:http://www.easck.com/p>