← 所有文章

MetricKit 重构:iOS 27 中的状态感知遥测

苹果在 WWDC 2026 上演示的应用,报告其滚动卡顿率为每秒 15 毫秒——这是按一整天使用情况平均得出的数字。可一旦按标签页拆分,同样的数据却讲述了截然不同的故事:一个标签页是 1 ms/s,另一个则是 71 ms/s。1 一个界面几乎完美无瑕;另一个用会上的话来说,正”经历严重的中断”。1 混合后的那个数字把这两个事实都掩盖了。Session 222”Meet the new MetricKit”讲述的,正是 iOS 27 如何弥合这一差距:对该框架 API 表面的彻底重构,以及一个全新的配套框架 StateReporting,它把整应用层面的现场指标转化为按状态划分的指标。现场遥测终于能回答每位性能工程师最先问出的那个问题:到底是哪个界面慢?

TL;DR

  • 在 iOS 27 中,MetricKit”已从头彻底重构,配备了语境丰富、表达力强的现代 Swift 优先 API”,会上介绍的每一项新能力都仅限于这套新 API。1
  • 入口是 MetricManager 类。应用在启动时 await metricReportsdiagnosticReports 这两个异步流,且两种报告类型都遵循 Codable,因此用一个 JSONEncoder 即可将它们直接发送到您的分析服务器。1
  • 报告是结构化的:intervalEntries 包含一条覆盖一整天的条目以及更小粒度的细分,按 .cpu.memory.display.gpu 等指标组组织,最终细化到 peakMemory 这样的单个值。1
  • iOS 27 的新数据:用于衡量渲染性能的 Metal 帧率指标、针对超出内存上限被终止的内存异常诊断,以及一个把单条崩溃诊断与指标趋势关联起来的崩溃 category1
  • 重头戏是 StateReporting 框架:报告应用所处的状态(当前标签页、实验分组、视图配置),MetricKit 便按状态聚合指标,用按界面划分的细分取代那一个混合数字。1

从头彻底重构

Watch on Apple Developer ↗

MetricKit 团队的工程师 Yonni 从 1:23 开始介绍 iOS 27 的这次重构。

MetricKit 的职责并未改变:它是性能工作流中”负责收集的那一环”,提供两类数据。指标告诉您某一性能领域总体上是在改善还是恶化;诊断则告诉您是哪条代码路径导致了问题。1 改变的是您接收这些数据的方式——一切都变了。会上说得很直白:在 iOS 27 中,该框架”已从头彻底重构,配备了语境丰富、表达力强的现代 Swift 优先 API”,而且”我今天要讲的所有改进都仅限于这套新 API”。1

新的入口是 MetricManager 类。您无需再注册 delegate 并解析 payload,而是通过 metricReports 属性以异步流的形式 await 报告。会上给出了两条直接的操作准则:在应用启动时完成设置,”以避免因订阅延迟而丢失任何数据”;并让 MetricManager 保持存活,”以便后续数据就绪时,这些流能持续投递报告”。1 苹果建议在应用一启动就在一个 detached task 或专门的 service 类中运行这部分工作。1

会上是在幻灯片中展示代码的,因此下面的代码片段只是与其描述相符的示意性调用形态;上线前请对照苹果文档核实确切的签名。

// Illustrative call shape based on session 222; verify against the docs.
let manager = MetricManager()

Task.detached {
    for await report in manager.metricReports {
        // Encode and ship, or inspect specific groups.
    }
}

过去,把报告发送到服务器意味着要处理不透明的 payload 数据。如今 MetricReport 值都遵循 Codable:”只需创建一个 JSONEncoder,然后对整份报告进行编码即可。”1 如果您想要的是某个具体值而非整份文档,报告本身是完全结构化的。您遍历 intervalEntries,它”包含一条覆盖一整天的聚合条目,以及在可用时提供的更小粒度的细分窗口”,每个窗口通常为几小时,且仅在确实存在对应指标时才出现。1 在每个区间内,指标按指标组组织,其中”每个组代表系统的一个方面,比如 .cpu.memory.display.gpu“。1 筛选到您关心的那个组(会上的示例取出了 memoryMetrics),再 switch 各个指标 case,即可取到 peakMemory 这样的单个值。1

iOS 27 中指标目录也有所扩充。除了启动耗时直方图(会上的示例显示大多数启动落在 510 到 540 毫秒之间)、卡顿、动画指标,以及 CPU、GPU、磁盘写入、网络传输等资源消耗之外,MetricKit 还新增了 Metal 帧率指标。会上称帧率是”游戏开发者理解渲染性能的一项关键指标”,并就优化方面推荐了”Find and fix performance issues in your Metal game”。1

请依赖 MetricKit 的启动指标,而不要自己去插桩测量启动。苹果测量启动耗时,是从用户点按应用图标的那一刻起,到首帧绘制为止——而这早于您的进程真正存在。3 自行编写的计时器只能在您的代码开始运行后才启动,因此它完全错过了 main 之前的那段窗口,也就低估了用户实际感受到的启动耗时。请去读取 MetricKit 提供的那份直方图,而不是去搭一个看不见关键部分的计时器。

诊断:回溯、内存异常与崩溃类别

指标告诉您某处出现了退化;诊断则告诉您退化在哪里。当出现问题时,比如崩溃或卡顿,”系统会在设备上捕获一份诊断”,而诊断报告会”把细节打包好,并通过 MetricKit 立即投递给您的应用”。1 许多诊断都包含回溯,展示事件发生时确切的调用栈。在会上的演示中,符号化后的回溯从系统代码中的线程起点开始,进入应用,止于应用的 submitReport() 函数——它标出了失败点,也指明了修复应针对的位置。1

崩溃诊断携带回溯、终止原因和异常类型。iOS 27 新增了一个终止 category,它”标明每次崩溃在指标中是如何被计入的”,因此”如果异常终止呈上升趋势,您可以直接把它们与单条诊断关联起来”。1 您仪表盘上的那条指标线,与它背后的单条崩溃报告,终于共享了一个关联键。

iOS 27 还新增了内存异常诊断:”当您的应用或扩展因超出内存上限而被终止时,您能获得更多关于发生了什么的洞察。”1 扩展被明确纳入范围,这对于任何要远程排查小组件或扩展内存被杀问题的人都很重要。

消费端与指标端如出一辙。您在自己的 MetricManager 实例上 await diagnosticReports,同样是在应用启动时于 detached task 或 service 类中进行,而 DiagnosticReport 值也遵循 Codable,可走同一条编码并发送的流水线。1 由于报告是结构化的,您可以对诊断 case 进行 switch:崩溃 case 会给出回溯、原因和 category,而卡顿 case 则可路由到不同的处理逻辑。1

// Illustrative call shape based on session 222; verify against the docs.
for await report in manager.diagnosticReports {
    switch /* diagnostic case */ {
    case /* crash */: break  // backtrace, reason, category
    case /* hang */:  break  // handle separately
    default:          break
    }
}

StateReporting:从一个混合数字到按界面划分的真相

上面的一切讲的仍是整应用层面的遥测,而整应用层面的遥测有其天花板。会上那个报销应用把问题讲得很具体。该应用把功能组织为一个 Reports 标签页和一个 Spending 标签页。一天下来,MetricKit 报告说在 5 分钟的滚动中共有 4.5 秒的卡顿时长:即 15 ms/s 的卡顿率。但这个数字是”覆盖全部应用使用情况的平均滚动卡顿率,哪怕用户是在 Reports 标签页和 Spending 标签页之间来回切换”。1 您知道应用有卡顿,却不知道卡在哪里。

全新的 StateReporting 框架消除了这种混合。状态是”您所定义的、用以描述应用配置或行为的信息,从而让 MetricKit 能够把指标按这些特征聚合”。1 当用户在标签页之间移动时,应用会报告每一次切换,而 MetricKit 会把这些状态与指标和诊断数据求交集。1

演示中带来回报的那一刻,正是这次重构的理由所在。指标不再是那一个混合的 15 ms/s 数字,而是按状态送达:Spending 标签页滚动起来”极其顺滑”,仅 1 ms/s,而 Reports 标签页则”飙升到 71 ms/s”。1 会上得出了那个混合数字永远无法支撑的结论:”Spending 标签页表现很棒!但 Reports 标签页正经历严重的中断,而那恰恰是您的优化精力应当聚焦之处。”1 一个数字变成了一份判决书,也变成了一张工单。

状态遵循一种切换模型,而非成对包裹。”没有起止配对——应用只是在任意给定时刻报告它当前所处的状况”,而 MetricKit 会追踪应用停留在每个状态中的时长。1

域、元数据,以及按状态编码

每个状态都被归入某个域,而域”描述应用的一项功能或一个区域”。一个域同一时刻只能持有一个活跃状态,而设置多个相互独立的域,则可让多个状态同时处于进行中。1 会上的示例是一次 A/B 实验:在实验性改动开启时,从数据库以小批量获取报销条目;关闭时则用更大的批量。把标签页状态与批量大小状态放进彼此独立的域,意味着”MetricKit 会为每个标签页、每种批量大小分别投递指标”。1 按界面划分的遥测与实验读数,出自同一条流水线,且来自现场。

会上把接入分为三步:导入 StateReporting 框架;创建一个域(”通常是一个反向 DNS 字符串”)并在设置 MetricManager 实例时注册它;然后在应用进入各个状态时报告切换,比如切换到由字符串”Reports”标识的状态。1 若想要更细的粒度,您可以用 ReportableMetadata 宏定义自己的 struct,用该元数据类型创建一个 StateReporter,并在报告切换时同时带上标签和您的自定义类型。会上的 ViewConfiguration 示例携带了一个 listSize 值,以及列表是否已排序的信息。1 再次提醒:会上是在幻灯片中展示这一流程的,并未给出完整签名,因此请把这些形态当作需要在文档中核实的内容,而非可以照抄的语法。

在接收端,报告新增了第二个维度。在尚未报告任何状态之前,您指标报告上的 stateEntries 属性是空的。接入之后,报告便携带一组 StateEntry 值,每个值持有”在该单个状态停留期间聚合得出的指标值”。1 对于服务器流水线,您可以把编码后的输出按域分组:将您 JSONEncoderuserInfo 属性上的 encodingFormatKey 键设为 byStateReportingDomain,编码后的报告便会”按报告中存在的每个域和状态分组”地同时呈现状态条目与区间条目。1

最佳实践,以及从何入手

会上以一番听来像是历经磨砺得来的 schema 设计建议作结。域应当被狭窄地限定在应用的单一区域。状态切换”应当代表稳定、有意义的阶段,而非转瞬即逝的 UI 事件”。1 设计每个状态时要做到:当退化出现时,仅凭这个状态就能给出足够信息去针对性地修复。同时也要克制住给一切都插桩的冲动:”状态过多会导致数据过于细碎,反而让人更难看清整体图景”,而且为了把开销降到最低,状态数量设有上限(会上未给出具体数字)。1 上线前,请用 Points of Interest 工具验证报告出的状态与您的预期相符。1

基数在元数据这一侧也是同样的陷阱。请把快速变化的状态值归入粗粒度的类别(小、中、大),而不要报告精确计数。在苹果 WWDC 2026 的性能实验室里,团队现场回答了关于 MetricKit 接入与更广泛功耗工作流的问题(记录于 苹果性能团队在 WWDC26 实验室所说的内容),他们指出记录”1,000 与 1,001 个条目”只增加成本却不带来洞察:这两个值落在同一性能区间,因此为每一个都设一个独立状态,买到的只有开销,别无其他。4 请挑选那些会改变行为的边界,把边界之间的一切都折叠起来。

收集只是整个系统的一半。会上直言”跨全部设备分析指标是一个数据科学问题”:您需要搭起一个服务器来摄取报告,沿着您关心的维度进行聚合,建立基线,并监控两个方向上的任何变动。1 Codable 报告与 byStateReportingDomain 编码的存在,正是为了喂养这样一条流水线。

对于既有的接入者,结尾的指示十分明确:”如果您正在使用 MXMetricManager API,请迁移到新的 MetricManager API,以用上这些全新能力。”1 苹果的文档现已正式确认这一迁移:MXMetricManager 自 27.0 起被标记为弃用,并附有指引”Use MetricManager instead”。2 本周期内,同样以 27 为界、分阶段强制实施的做法也出现在别处,包括 Image Playground 的 iOS 27 中 ImageCreator 的弃用,在那里弃用警告会在公开发布时转为硬性中断。会上的每一项进展都活在这套新 API 中,而会上也把它们称为”该框架的未来”。1

FAQ

iOS 27 中 MetricKit 究竟变了什么?

该框架已用现代 Swift 优先 API 重构。入口是全新的 MetricManager 类;指标报告与诊断报告以可 await 的异步流(metricReportsdiagnosticReports)形式送达;报告遵循 Codable,可直接进行 JSON 编码;其结构也可在代码中通过 intervalEntries 和指标组导航。iOS 27 还新增了 Metal 帧率指标、内存异常诊断、一个把崩溃诊断与指标计入方式关联起来的崩溃 category,以及用于按状态划分指标的 StateReporting 框架。1

StateReporting 如何决定哪些指标归属于哪个状态?

您的应用报告切换:它正在进入的状态,以及您所定义的某个域。MetricKit 追踪应用停留在每个状态中的时长,并把这段时间内的指标值聚合起来。这里没有起止配对;应用只是在任意给定时刻报告它当前所处的状况。每个状态随后都会在指标报告中获得属于自己的 StateEntry1

我能否同时追踪多个维度,比如界面和实验分组?

可以。每个域同一时刻只能持有一个活跃状态,但相互独立的域可以并发运行。会上那个报销应用把当前标签页放进一个域,把数据库批量大小实验放进另一个域,MetricKit 便为每个标签页、每种批量大小分别投递指标。1

我应该把每一个 UI 事件都报告为一个状态吗?

不应该。会上建议状态应代表稳定、有意义的阶段,而非转瞬即逝的 UI 事件;域应被狭窄地限定在应用的单一区域;总体上也要克制:状态过多会让数据更难解读,而且系统为了把开销降到最低,对状态数量设有上限。上线前请用 Points of Interest 工具验证您的状态。1

我必须放弃 MXMetricManager 吗?

会上的指引是从 MXMetricManager 迁移到新的 MetricManager API,因为所涵盖的每一项新能力(异步流、Codable 报告、状态感知指标、新的指标与诊断类型)都仅限于这套新 API。1


MetricKit 是今年这个两段式故事的现场那一半:Instruments 在实验室里把卡顿展示给您看,而状态感知的 MetricKit 则告诉您真实用户在哪些界面遭遇卡顿——实验室那一侧的内容见 Instruments 27 与应用响应性。真正能修复一个 71 ms/s 标签页的渲染工作,见 iOS 27 中的 SwiftUI 性能与互操作。而混合平均值之所以一开始就会误导人,正是 性能盲点 探讨的主题。整个系列的中心枢纽是 Apple 生态系统系列

参考资料


  1. Apple, WWDC 2026 session 222, Meet the new MetricKit. Source for the iOS 27 ground-up rebuild and Swift-first API framing, the MetricManager entry point and the metricReports / diagnosticReports async streams, Codable reports and JSONEncoder usage, intervalEntries and metric groups (.cpu, .memory, .display, .gpu, peakMemory), the Metal frame rate metric, memory exception diagnostics, the crash termination category, the submitReport() backtrace walkthrough, the StateReporting framework (domains, transition model, StateReporter, ReportableMetadata, stateEntries, byStateReportingDomain via encodingFormatKey), the expense-app demo numbers (15 ms/s blended; 1 ms/s Spending tab versus 71 ms/s Reports tab), the state best practices and Points of Interest validation, and the guidance to migrate from MXMetricManager to MetricManager

  2. Apple Developer Documentation: MXMetricManager. Marked deprecated as of 27.0, with the guidance “Use MetricManager instead.” 

  3. Apple Developer Documentation: Reducing your app’s launch time. Launch time is measured from the moment the user taps the app icon to the first frame drawn, before the app’s process exists. 

  4. Apple, WWDC 2026 performance group lab, session 8003. Paraphrased from a locally transcribed recording; no official transcript is published. The team advised bucketing fast-changing state metadata into coarse categories, noting that recording “1,000 versus 1,001 items” adds cost without insight. 

相关文章

Apple 性能团队在 WWDC26 实验室里说了什么

Apple 的 Power & Performance 团队在 WWDC26 现场回答开发者的问题。关于 MetricKit、Power Profiler、热管理与 AI 资源争用的实战指引。

3 分钟阅读

Instruments 27 在 App 响应性方面的新功能

Instruments 27 新增了 Top Functions、Run Comparisons、Swift executors 工具以及全新的 Inspector 面板,用于诊断 CPU、actor 与系统调用造成的卡顿。

4 分钟阅读

从76到100:实现Lighthouse满分

一个个人作品集网站如何从移动端Lighthouse性能评分76分、CLS为0.493,提升到所有类别均达到100/100/100/100的满分。

3 分钟阅读