MetricKit 全面重建:iOS 27 中的状态感知遥测
Apple 在 WWDC 2026 的演示 App 报告的滚动卡顿率为每秒 15 毫秒,这是整天使用量的平均值。然而按标签页拆分后,同一份数据讲的却是截然不同的故事:一个标签页是 1 ms/s,另一个则是 71 ms/s。1一个画面近乎完美,另一个用该场次的说法是「正经历严重中断」。1这个混合后的数字把两件事都掩盖了。第 222 场「Meet the new MetricKit」讲述的正是 iOS 27 如何弥合这道落差:彻底重建框架的 API 界面,并推出全新的配套框架 StateReporting,把整个 App 的现场指标转换成按状态区分的指标。现场遥测终于能回答每位性能工程师最先问的问题:到底是哪个画面慢?
TL;DR
- 在 iOS 27 中,MetricKit「已从头重建,采用情境丰富、表达力强的现代 Swift 优先 API」,而该场次介绍的每一项新功能都是这套新 API 独有的。1
- 入口是
MetricManager类。App 在启动时 awaitmetricReports与diagnosticReports两个异步流,而这两种报告类型都符合Codable,因此用一个JSONEncoder就能直接把它们发送到您的分析服务器。1 - 报告是结构化的:
intervalEntries包含一条整日的条目,以及较小的细分区间,并按.cpu、.memory、.display、.gpu等指标分组来组织,最终可深入到peakMemory这类单个数值。1 - iOS 27 的新数据:用于衡量渲染性能的 Metal 帧率指标、针对超出内存上限而终止的内存异常诊断,以及一个把单次崩溃诊断与您的指标趋势串联起来的崩溃
category。1 - 最受瞩目的功能是
StateReporting框架:报告您 App 所处的状态(当前标签页、实验分组、视图配置),MetricKit 便会按状态汇总指标,把单一的混合数字换成按画面区分的细目。1
从头重建
MetricKit 团队的工程师 Yonni 从 1:23 开始介绍 iOS 27 的重建。
MetricKit 的任务并未改变:它是性能工作流程中「负责采集的部分」,提供两类数据。指标告诉您某个性能方面整体上是在改善还是恶化;诊断则告诉您是哪条代码路径造成了问题。1改变的是您接收这些数据的整套方式。该场次说得很直接:在 iOS 27 中,这个框架「已从头重建,采用情境丰富、表达力强的现代 Swift 优先 API」,而且「我今天要谈的所有进展,都是这套全新 API 独有的」。1
新的入口是 MetricManager 类。您不再需要注册 delegate 并解析 payload,而是通过 metricReports 属性以异步流的形式 await 报告。有两条操作原则直接出自该场次:在 App 启动时就完成设置,「以避免因延迟订阅而造成任何数据丢失」,并让 MetricManager 保持存活,「这样流才能在后续数据就绪时持续传递报告」。1Apple 建议在 App 一启动时,就用一个 detached task 或专属的服务类来执行这些工作。1
该场次是在幻灯片上展示代码,因此以下的片段是符合其描述的示意调用形式;在正式发布前,请对照 Apple 的文档确认确切的签名。
// 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),再对各指标 case 进行 switch,便能取得 peakMemory 这类单个数值。1
iOS 27 的指标目录也在扩充。除了启动时间直方图(该场次示例显示大多数启动落在 510 到 540 毫秒之间)、卡顿、动画指标,以及 CPU、GPU、磁盘写入、网络传输等资源消耗之外,MetricKit 还新增了 Metal 帧率指标。该场次称帧率是「游戏开发者理解渲染性能的关键指标」,并在优化方面指向「Find and fix performance issues in your Metal game」。1
诊断:回溯追踪、内存异常与崩溃分类
指标告诉您有东西退步了,诊断则告诉您退步在哪里。当出问题时,例如崩溃或卡顿,「系统会在设备上捕获一条诊断」,而一份诊断报告会「把细节打包好,并通过 MetricKit 立即发送到您的 App」。1许多诊断都包含回溯追踪,呈现事件发生当下的确切调用栈。在该场次的逐步演示中,符号化后的回溯追踪从系统代码中的线程起点开始,跨入 App,最后停在 App 的 submitReport() 函数,标示出失败点,也就是修复应该瞄准的地方。1
崩溃诊断会带有回溯追踪、终止原因与异常类型。iOS 27 新增的终止 category「标示出每次崩溃在指标中是如何计入的」,因此「若异常终止呈现上升趋势,您可以把它们直接与单个诊断做关联」。1您仪表板上的指标折线,以及其背后的单个崩溃报告,终于共用同一把钥匙。
iOS 27 也新增了内存异常诊断:「当您的 App 或扩展因超出内存上限而被终止时,您能对发生了什么获得更多洞察。」1扩展明确涵盖在范围内,这对任何要远程调试小组件或扩展内存被终止的人而言都很重要。
接收端与指标端如出一辙。您在自己的 MetricManager 实例上 await diagnosticReports,同样在 App 启动时于 detached task 或服务类中进行,而 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:从单一混合数字到按画面呈现的真相
以上所有内容描述的仍是整个 App 的遥测,而整个 App 的遥测有其天花板。该场次的报销 App 把问题具体化了。这个 App 把功能组织成一个 Reports 标签页与一个 Spending 标签页。在一天当中,MetricKit 报告了 5 分钟滚动里共 4.5 秒的卡顿时间:卡顿率为 15 ms/s。但这个数字是「整个 App 使用过程的平均滚动卡顿率,即使有人在 Reports 标签页与 Spending 标签页之间来回切换也一样」。1您知道 App 会卡,却不知道卡在哪里。
全新的 StateReporting 框架去除了这种混合。状态是「您定义的信息,用来描述 App 的配置或行为,好让 MetricKit 能以这些特征为函数来汇总指标」。1当用户在标签页之间移动时,App 会报告每一次转换,而 MetricKit 会把这些状态与指标及诊断数据相交。1
演示中的成果,正是让这场重建显得值得的时刻。指标不再是单一的混合 15 ms/s 数字,而是按状态送达:Spending 标签页滚动起来「顺得不可思议」,为 1 ms/s,而 Reports 标签页则「飙升到 71 ms/s」。1该场次下了混合数字永远无从支撑的结论:「Spending 标签页表现得很棒!但 Reports 标签页正经历严重中断,而那正是您的优化精力该聚焦之处。」1一个数字变成了一项判断与一份工作清单。
状态遵循的是转换模型,而非成对括起。「没有起止配对——App 在任一时刻报告它当下所处的状况」,而 MetricKit 会追踪 App 停留在每个状态的时间长短。1
领域、元数据与按状态编码
每个状态都归属于一个领域(domain),它「描述 App 的某项功能或某个区域」。一个领域同一时间只能持有一个活动状态,而不同的领域则能让多个状态同时进行中。1该场次的示例是一项 A/B 实验:开启实验性变更时,支出数据以小批次从数据库取出;关闭时则用较大批次。把标签页状态与批次大小状态放在不同的领域,意味着「MetricKit 会为每个标签页与每种批次大小各自交付指标」。1按画面的遥测与实验读数,出自同一条流程,且来自现场。
该场次的接入有三个步骤:导入 StateReporting 框架,创建一个领域(「通常是一个反向 DNS 字符串」)并在设置 MetricManager 实例时注册它,接着在 App 进入每个状态时报告转换,例如转换到以字符串「Reports」标识的状态。1若需更细的粒度,您可用 ReportableMetadata 宏定义自己的 struct,以该元数据类型创建一个 StateReporter,并在报告转换时同时带上标签与您的自定义类型。该场次的 ViewConfiguration 示例带有一个 listSize 值,以及列表是否已排序的信息。1再次提醒:该场次是在幻灯片上展示这套流程,并未提供完整签名,因此请把这个形式当成需在文档中确认的东西,而非可直接复制的语法。
在接收端,报告多出了第二个维度。在报告任何状态之前,您指标报告上的 stateEntries 属性是空的。接入之后,报告会带有 StateEntry 值,每一个都持有「在该单个状态所花时间内汇总的指标数值」。1对于服务器流程,您可以把编码输出按领域分组:在您 JSONEncoder 的 userInfo 属性上把 encodingFormatKey 键设为 byStateReportingDomain,编码后的报告便会把 state entries 与 interval entries 都「按报告中存在的每个领域与状态分组」呈现。1
最佳实践,以及从何着手
该场次以一段读来像是来之不易的结构设计建议作结。领域应狭隘地限定在 App 的单一区域。状态转换「应代表稳定且有意义的阶段,而非短暂的 UI 事件」。1请把每个状态设计成:当退步出现时,仅凭状态本身就能给您足够的信息去瞄准修复。同时,请克制把每件事都加上度量的冲动:「状态太多可能导致数据过于细碎,反而更难解读整体面貌」,而且状态数量设有上限,以将额外开销降到最低(该场次并未给出数字)。1在发布前,请用 Points of Interest 工具验证所报告的状态是否符合您的预期。1
采集端只是整个系统的一半。该场次直言「跨所有设备分析指标是一个数据科学问题」:您要搭起一台服务器来吸收报告,沿着您关心的维度汇总,建立基线,并监看两个方向上的变动。1Codable 报告与 byStateReportingDomain 编码的存在,正是为了喂养这条流程。
对于既有的采用者,结尾的指示很明确:「如果您正在使用 MXMetricManager API,请迁移到新的 MetricManager API,以充分利用所有这些新功能。」1该场次的每一项进展都活在这套新 API 中,而该场次把它们呈现为「框架的未来」。1
FAQ
iOS 27 的 MetricKit 究竟改了什么?
这个框架以现代 Swift 优先的 API 重建。入口是新的 MetricManager 类;指标与诊断报告以可 await 的异步流送达(metricReports、diagnosticReports);报告符合 Codable,可直接做 JSON 编码;而其结构可在代码中通过 intervalEntries 与指标分组来浏览。iOS 27 还新增了 Metal 帧率指标、内存异常诊断、一个把崩溃诊断与指标计入串联起来的崩溃 category,以及用于按状态指标的 StateReporting 框架。1
StateReporting 如何判定哪些指标属于哪个状态?
您的 App 会报告转换:它正要进入的状态,落在您所定义的某个领域内。MetricKit 会追踪 App 停留在每个状态的时间长短,并把该段时间内的指标数值汇总起来。没有起止配对;App 只是在任一时刻报告它当下所处的状况。每个状态接着会在指标报告中各自取得一条 StateEntry。1
我能不能同时追踪多个维度,例如画面与实验分组?
可以。每个领域同一时间能持有一个活动状态,但不同的领域是并行运行的。该场次的报销 App 把当前标签页放在一个领域,把数据库批次大小实验放在另一个领域,而 MetricKit 会为每个标签页与每种批次大小各自交付指标。1
我该把每个 UI 事件都当成状态报告吗?
不该。该场次建议采用代表稳定、有意义阶段的状态,而非短暂的 UI 事件;领域应狭隘地限定在 App 的单一区域;整体上也要有所节制:状态太多会让数据更难解读,而系统也对状态数量设有上限以将额外开销降到最低。发布前,请用 Points of Interest 工具验证您的状态。1
我一定得离开 MXMetricManager 吗?
该场次的建议是从 MXMetricManager 迁移到新的 MetricManager API,因为其中涵盖的每一项新功能(异步流、Codable 报告、状态感知指标、新的指标与诊断类型)都是这套新 API 独有的。1
今年 MetricKit 是一则由两部分组成的故事中属于现场的那一半:Instruments 在实验室里让您看见卡顿,而状态感知的 MetricKit 则告诉您对真实用户而言是哪些画面在卡——实验室那一侧的内容收录于 Instruments 27 与 App 响应性。真正能修好一个 71 ms/s 标签页的渲染工作,则谈于 iOS 27 中的 SwiftUI 性能与互通。至于为何混合后的平均值一开始就会误导人,正是 性能盲点 的主题。完整的系列主页为 Apple Ecosystem Series。
References
-
Apple, WWDC 2026 session 222, Meet the new MetricKit. Source for the iOS 27 ground-up rebuild and Swift-first API framing, the
MetricManagerentry point and themetricReports/diagnosticReportsasync streams,Codablereports andJSONEncoderusage,intervalEntriesand metric groups (.cpu,.memory,.display,.gpu,peakMemory), the Metal frame rate metric, memory exception diagnostics, the crash terminationcategory, thesubmitReport()backtrace walkthrough, theStateReportingframework (domains, transition model,StateReporter,ReportableMetadata,stateEntries,byStateReportingDomainviaencodingFormatKey), 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 fromMXMetricManagertoMetricManager. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩
