由于自己待的小厂,没有做过APM(Application Performance Management,应用性能管理)相关的东西,被问及这方面内容的时候有些懵,于是读了下这本书,稍微补了点APM的知识。
整本书薄薄的一百多页,两天就能读完,把iOS的APM分为以下六个部分:
- 日志监控
- 崩溃监控
- 卡顿监控
- 网络监控
- 硬件监控
- 内存泄露监控
日志监控
解决的问题:
手机不连上Xcode就没法查看日志
原理:
用NSLog打的日志都进入了ASL(Apple System Log)中。ASL相当于系统存放日志的数据库。苹果提供了API可以从ASL里捞出你想要的日志。
崩溃监控
解决的问题:
QA发现崩溃后跑来找开发,结果无法重现
原理:
- 对于未捕获异常崩溃,利用
NSSetUncaughtExceptionHandler
在崩溃前的最后一刻留下遗言 - 对于底层信号崩溃,利用内核提供的API
signal
,给不同的崩溃信号设置处理函数
卡顿监控
解决的问题:
卡顿发生的很随机,且卡顿原因无法捕捉
原理:
主线程发生了卡顿,一定是在主Runloop的一次迭代中,执行了一个很长时间的任务,导致它迟迟不能干完活去休息。
- Runloop的解决方案
可以利用信号量,或者定时器的方式(书中利用了信号量)。监测到卡顿后记录下调用堆栈,就可以上报服务端了。
1.1 利用信号量
主线程观察主Runloop的状态变化,每次状态有变化signal一个信号量。子线程开一个死循环,循环一开始wait这个信号量。在超时阈值n秒内等到信号量的话说明这次不卡,开启下次迭代。如果n秒内没等到信号量而超时,说明可能有点卡,记一笔,连续5次这样的话就算一次卡顿。
1.2 利用定时器
主线程观察主Runloop的状态变化,在AfterWaiting(Runloop醒来准备开始干活了)状态记录一个时间戳x,在BeforeWaiting(Runloop干完活准备去休息了)状态将时间戳x清零。子线程开启定时器,每隔0.5秒算出当前时间戳和x之间的差值,如果差值大于某个阈值则认为发生了卡顿。 - GCD的解决方案
开个子线程,里面开个死循环,循环一开始子线程通过GCD扔一个block到main queue去执行,扔完自己就去睡个n秒(n为卡顿阈值)。在醒来前block要是还没被执行到的话,就认为发生了卡顿。
网络监控
解决的问题:
必须开Charles才能看到网络请求和返回。
原理:
利用NSURLProtocol拦截请求
硬件监控
解决的问题:
出现性能问题时看不到各项硬件指标
原理:
内核提供了API可以拿到CPU,内存,流量,温度等信息。
帧率FPS监控的原理:利用CADisplayLink
算出某一段时间n(n >= 1)内刷新的帧数x,FPS = x / n
内存泄露监控
解决的问题:
必须要Instruments才能查内存泄露
原理:
大多数情况下,一个UIViewController
被pop或dismiss后,它的view以及subviews等会很快释放。所以在一个UIViewController
被pop或dismiss后的一段时间内,看看它的view以及subviews还在不在。