在不少性能讨论里,iOS CPU 占用率常被当成一个直观指标:数值高了就是问题,低了就算安全。 但真正参与过线上问题排查之后,会发现这种理解过于粗糙。
我第一次真正被 CPU 占用率困住,并不是因为它"飙到了一个夸张的数",而是因为------ 它看起来不高,却始终维持在一个不该出现的水平。
CPU 问题,往往从一句模糊的反馈开始
那次的反馈很典型:页面不卡,但手机有点热,用一会儿就掉电。
如果只从功能角度看,这几乎没有可操作的信息。 但经验告诉我,这类问题十有八九和 CPU 使用方式有关。
Instruments 能看到很多,但前提是你知道该看什么
排查 CPU 的第一反应,通常还是 Instruments。
Time Profiler 很快能给出调用栈、线程分布、函数耗时。 在短时间测试里,一切都显得"还行":
- 主线程没有明显阻塞
- CPU 峰值不高
- 没有死循环
如果就此下结论,很容易认为"CPU 没问题"。
当 CPU 问题不发生在瞬间
问题出在这里: 这次的 CPU 异常,并不是某一次操作触发的,而是持续存在的状态。
单次 Time Profiler 很难说明:
- 为什么 CPU 一直处在偏高区间
- 哪些操作在"慢慢累积"
- 为什么退出页面后 CPU 没有明显回落
这类问题,本质上和时间有关。
把 CPU 放回真实使用环境里看
后来我换了一种方式,不再急着抓调用栈,而是先观察 CPU 在真实使用过程中的变化。
这一步,我用的是 克魔(KeyMob)。
原因并不复杂: 它可以在真机上持续监控 CPU 占用率,并把变化过程完整记录下来,而不是只看某一个采样点。
一条 CPU 曲线,比一个数值更有说服力
当我连续使用 App 十几分钟之后,CPU 的问题才真正显现出来:
- CPU 没有明显峰值
- 但均值始终偏高
- 页面切换后没有明显回落
- 前后台切换时存在短暂抖动
这些现象,单独看都不算"异常", 但组合在一起,就很难忽略了。
回到 Instruments,这一次目标很明确
有了趋势之后,再回到 Instruments,事情就变得简单多了。
我开始刻意盯着几段流程:
- 页面切换时的线程活动
- 某些异步任务是否在后台持续运行
- 定时逻辑是否按预期结束
最终发现,一个看似无害的轮询逻辑,在页面退出后并没有停止。
如果没有前面的长期观察,这个点很容易被忽略。
WebView 和 CPU,经常是"低调的消耗者"
在另一个项目中,CPU 占用问题并不来自 Native。
通过 Safari Inspector,我发现某些 WebView 页面在退到后台后,仍然有 JS 定时任务在执行。 每一次执行都不重,但长期叠加,对 CPU 的影响非常稳定。
这类问题,用纯 Native 的 CPU 分析工具很难察觉。
网络行为,有时会把 CPU 问题放大
还有一次 CPU 异常,最终是通过 Charles 才理清楚的。
抓包发现:
- 接口在弱网环境下频繁重试
- 数据解析逻辑被反复触发
- 日志输出量明显增加
单次看,这些行为都算不上严重。 但在真实使用中,它们共同维持了一个不必要的 CPU 活跃状态。
CPU 占用率,不是一个孤立指标
经历这些排查之后,我对 CPU 占用率的理解发生了变化:
- 它不是用来"判定对错"的
- 而是用来提示"行为是否合理"
在工程实践中,我更关心的是:
- CPU 在什么阶段升高
- 操作结束后是否能回落
- 是否存在长期活跃的线程或任务
KeyMob 在这里的价值,并不是替代 Instruments,而是帮我找到该用 Instruments 看哪里。
实际工作中常用的一种组合方式
现在在处理 CPU 相关问题时,我通常会这样配合工具:
- KeyMob:观察真机 CPU 长期变化
- Instruments:定位具体消耗点
- Safari Inspector:检查 WebView 行为
- Charles:分析网络引发的 CPU 活动
- Xcode:验证逻辑与线程状态
这样做的好处是,不会被单一视角误导。