在不少性能讨论里,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:验证逻辑与线程状态

这样做的好处是,不会被单一视角误导。