← 所有文章

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 在啟動時 await metricReportsdiagnosticReports 這兩個非同步串流,而兩種報告型別都是 Codable,因此用一個 JSONEncoder 就能直接把它們送往您的分析伺服器。1
  • 報告是結構化的:intervalEntries 包含一筆全天的條目外加數筆較小的細分區段,並組織成 .cpu.memory.display.gpu 等 metric 群組,最終可深入到 peakMemory 這類個別數值。1
  • iOS 27 的新資料:用於了解算繪效能的 Metal 影格率 metric、用於記憶體上限終止情況的記憶體例外 diagnostic,以及把個別當機 diagnostic 連回您 metric 趨勢的當機 category1
  • 最受矚目的功能是 StateReporting 框架:回報您的 app 所處的狀態(作用中的分頁、實驗組別、檢視設定),MetricKit 便會依狀態彙整指標,把單一的混合數字替換成依畫面區分的細目。1

從頭重新打造

Watch on Apple Developer ↗

MetricKit 團隊的工程師 Yonni 從 1:23 起介紹 iOS 27 的這次重建。

MetricKit 的任務並未改變:它是效能工作流程中的「收集環節」,提供兩種資料。Metric 告訴您某個效能面向整體上是在改善還是惡化;diagnostic 則告訴您是哪段程式碼路徑造成了問題。1改變的是您接收這些資料的一切方式。議程說得很直白:在 iOS 27 中,這套框架「已從頭重新打造,提供脈絡豐富且富表達力的現代 Swift 優先 API」,而且「我今天要談的所有進展,都是這套全新 API 獨有的」。1

新的進入點是 MetricManager 類別。您不再需要註冊一個 delegate 並剖析 payload,而是透過 metricReports 屬性以非同步串流的方式 await 報告。兩條操作原則直接出自議程:在 app 啟動時就完成設定,「以避免因延遲訂閱而造成任何資料遺失」,並讓 MetricManager 保持存活,「如此串流才能在後續資料就緒時持續傳遞報告」。1Apple 建議在 app 一啟動時,就在一個 detached task 或專屬的 service 類別中執行這項工作。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在每個 interval 之內,指標會組織成 metric 群組,其中「每個群組代表系統的一個面向,例如 .cpu.memory.display.gpu」。1篩選到您關注的群組(議程的範例取出的是 memoryMetrics),再對 metric 的各個 case 進行 switch,即可取得 peakMemory 這樣的個別數值。1

iOS 27 的 metric 目錄也有所擴充。除了啟動時間直方圖(議程的範例顯示多數啟動落在 510 到 540 毫秒之間)、卡頓、動畫指標,以及 CPU、GPU、磁碟寫入與網路傳輸等資源消耗外,MetricKit 還新增了一個 Metal 影格率 metric。議程稱影格率是「遊戲開發者了解算繪效能的關鍵 metric」,並指引讀者參閱「Find and fix performance issues in your Metal game」以了解最佳化的一面。1

請善用 MetricKit 的啟動 metric,而非自行對啟動進行檢測。Apple 測量啟動時間的起點,是使用者點按 app 圖示的那一刻,直到第一個影格繪製出來為止,而這甚至發生在您的行程還不存在之前。3手刻的計時器只能在您的程式碼開始執行後才啟動,因此它完全錯過了 main 之前的那段時間,進而低估了使用者實際感受到的啟動時間。請改為閱讀 MetricKit 提供的直方圖,而不要打造一個看不見關鍵環節的計時器。

Diagnostic:回溯追蹤、記憶體例外與當機分類

Metric 告訴您某處退步了;diagnostic 則告訴您是在哪裡。當發生問題時,例如當機或卡頓,「系統會在裝置上擷取一份 diagnostic」,而一份 diagnostic 報告會「把細節打包,並透過 MetricKit 立即傳遞給您的 app」。1許多 diagnostic 都包含 backtrace,顯示事件發生當下的確切呼叫堆疊。在議程的逐步演示中,符號化後的 backtrace 從系統程式碼中的執行緒起點開始,跨入 app,並停在 app 的 submitReport() 函式,而這正標示出失敗的位置,也是修正應鎖定之處。1

當機 diagnostic 帶有 backtrace、終止原因與例外型別。iOS 27 新增了終止 category,它「指出每次當機在指標中是如何被計入的」,因此「如果異常終止呈上升趨勢,您就能把它們直接與個別 diagnostic 相互對照」。1您儀表板上的那條 metric 線,與其背後的個別當機報告,終於共享了一把鑰匙。

iOS 27 也新增了記憶體例外 diagnostic:「當您的 app 或 extension 因超出記憶體上限而被終止時,您可以對發生了什麼事獲得更多洞察。」1Extension 明確被納入範圍,這對任何需要遠端除錯 widget 或 extension 記憶體終止情況的人來說都很重要。

消費端與 metric 端如出一轍。您在 MetricManager 實例上 await diagnosticReports,同樣在 app 啟動時於 detached task 或 service 類別中進行,而 DiagnosticReport 值也是 Codable,適用同一條編碼並送出的管線。1由於報告是結構化的,您可以對 diagnostic 的各個 case 進行 switch:當機 case 會產生 backtrace、原因與 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 則把這些狀態與 metric 及 diagnostic 資料交集起來。1

示範中的回報成果,正是足以證明這場重新打造價值的時刻。指標不再是單一的混合 15 ms/s 數字,而是依狀態送達:Spending 分頁滑動起來「順暢得不可思議」,僅 1 ms/s,而 Reports 分頁則「飆升至 71 ms/s」。1議程得出了那個混合數字永遠無法支撐的結論:「Spending 分頁表現得很棒!但 Reports 分頁正經歷嚴重的中斷,而這正是您的最佳化努力應當聚焦之處。」1一個數字化為一份判決,也化為一張工單。

狀態遵循的是轉換模型,而非成對括住。「沒有起點或終點的配對——app 回報的是它在任一時間點所處的狀態」,而 MetricKit 會追蹤 app 停留在每個狀態的時間長度。1

領域、metadata 與依狀態編碼

每個狀態都歸屬於某個 domain,而 domain「描述 app 的某項功能或某個區域」。一個 domain 在同一時間只能持有一個作用中的狀態,而不同的 domain 則讓多個狀態能同時並行。1議程的範例是一項 A/B 實驗:當實驗性的變更開啟時,費用會以小批次從資料庫擷取;關閉時,則為較大的批次。把分頁狀態與批次大小狀態放在不同的 domain 中,意味著「MetricKit 會為每個分頁與每種批次大小分別傳遞指標」。1依畫面區分的遙測,以及來自同一條管線的實驗讀數,皆在現場取得。

議程中的採用分為三個步驟:匯入 StateReporting 框架,建立一個 domain(「通常是一段反向 DNS 字串」)並在設定 MetricManager 實例時加以註冊,再於 app 進入每個狀態時回報轉換,例如轉換到一個以字串「Reports」標識的狀態。1若需更細的粒度,您可以用 ReportableMetadata 巨集定義自己的 struct,以該 metadata 型別建立一個 StateReporter,並在回報轉換時同時帶上標籤與您的自訂型別。議程的 ViewConfiguration 範例帶有一個 listSize 值,以及該清單是否已排序的資訊。1同樣地:議程是在投影片上展示這個流程而未附上完整簽章,因此請把這個形式視為需要在文件中確認的東西,而非可直接照抄的語法。

在接收端,報告增添了第二個軸向。在回報任何狀態之前,您 metric 報告上的 stateEntries 屬性是空的。採用之後,報告會帶有 StateEntry 值,每一筆都持有「橫跨停留在該個別狀態之時間所彙整的指標數值」。1對於伺服器管線,您可以把編碼後的輸出依 domain 分組:在您 JSONEncoderuserInfo 屬性上把 encodingFormatKey 鍵設為 byStateReportingDomain,編碼後的報告便會同時呈現 state entries 與 interval entries,且「依報告中存在的每個 domain 與狀態分組」。1

最佳實務,以及該從何處著手

議程在收尾時給出的指引,讀來宛如得來不易的 schema 設計建言。Domain 應緊密地限縮在單一 app 區域。狀態轉換「應代表穩定、有意義的階段,而非短暫的 UI 事件」。1請把每個狀態設計成:當退步出現時,光憑該狀態就能給您足夠的資訊來鎖定修正。並請克制把一切都拿來檢測的衝動:「過多的狀態可能導致資料過於細碎,反而讓人更難解讀整體樣貌」,而狀態數量設有上限,正是為了將額外負擔降到最低(議程並未給出確切數字)。1出貨前,請用 Points of Interest instrument 驗證所回報的狀態符合您的預期。1

基數(cardinality)是 metadata 端同樣的陷阱。請把快速變動的狀態數值歸入較粗的類別(小、中、大),而非回報精確的計數。在 Apple 的 WWDC 2026 效能實驗室,團隊現場回答了關於 MetricKit 採用以及更廣泛功耗工作流程的提問(收錄於Apple 效能團隊在 WWDC26 實驗室所說的內容),他們指出記錄「1,000 對比 1,001 個項目」只會增加成本卻無洞察可言:這兩個數值落在同一個效能區間,因此為每一個都設立一個獨立狀態,買到的只有額外負擔而別無其他。4請挑出會改變行為的邊界,並把其間的一切都收攏起來。

收集端只占了系統的一半。議程直言不諱地指出「分析所有裝置上的指標是一個資料科學問題」:您要架起一台伺服器來吸收報告,沿著您關注的維度進行彙整,建立基準,並監看任一方向的變動。1Codable 報告與 byStateReportingDomain 編碼的存在,正是為了餵養這樣一條管線。

對於既有的採用者,收尾的指示十分明確:「如果您正在使用 MXMetricManager API,請遷移到新的 MetricManager API,以善用所有這些新功能。」1Apple 的文件如今已將這項遷移正式化:MXMetricManager 自 27.0 起被標示為 deprecated,並附上「Use MetricManager instead」的指引。2本週期同樣分階段、以 27 為界線的強制做法也出現在其他地方,包括 Image Playground 的 ImageCreator 在 iOS 27 中被棄用,在那裡,棄用警告會在正式公開發佈時轉為硬性中斷。新的 API 是議程中每一項進展的所在,而議程把它們呈現為「這套框架的未來」。1

FAQ

MetricKit 在 iOS 27 中究竟有何改變?

這套框架已用現代 Swift 優先的 API 重新打造。進入點是全新的 MetricManager 類別;metric 與 diagnostic 報告以可 await 的非同步串流(metricReportsdiagnosticReports)送達;報告都是 Codable,可直接進行 JSON 編碼;而其結構也可透過 intervalEntries 與 metric 群組在程式碼中走訪。iOS 27 還新增了 Metal 影格率 metric、記憶體例外 diagnostic、把當機 diagnostic 連結到指標計入的當機 category,以及用於依狀態區分指標的 StateReporting 框架。1

StateReporting 如何決定哪些指標歸屬於哪個狀態?

您的 app 回報轉換:它正轉移到的狀態,以及這發生在您所定義的某個 domain 之內。MetricKit 會追蹤 app 停留在每個狀態的時間長度,並把該段時間內的指標數值彙整起來。沒有起點/終點的配對;app 只是回報它在任一時間點所處的狀態。隨後,每個狀態都會在 metric 報告中獲得自己的 StateEntry1

我能同時追蹤不只一個維度嗎,例如畫面與實驗組別?

可以。每個 domain 在同一時間只能持有一個作用中的狀態,但不同的 domain 可以並行運作。議程的費用 app 把作用中的分頁放在一個 domain,把資料庫批次大小實驗放在另一個,而 MetricKit 便為每個分頁與每種批次大小分別傳遞指標。1

我該把每個 UI 事件都回報為一個狀態嗎?

不該。議程建議採用代表穩定、有意義階段的狀態,而非短暫的 UI 事件;domain 應緊密限縮在單一 app 區域;整體上更要有所節制:過多的狀態會讓資料更難解讀,而系統也對狀態數量設有上限以將額外負擔降到最低。出貨前,請用 Points of Interest instrument 驗證您的狀態。1

我必須捨棄 MXMetricManager 嗎?

議程的指引是從 MXMetricManager 遷移到新的 MetricManager API,因為所涵蓋的每一項新功能(非同步串流、Codable 報告、具狀態感知的指標、新的 metric 與 diagnostic 型別)都是新 API 組合所獨有的。1


MetricKit 是今年這個兩部曲故事中的現場那一半:Instruments 在實驗室裡讓您看見卡頓,而具狀態感知的 MetricKit 則告訴您真實使用者究竟在哪些畫面遇到卡頓,實驗室端的內容請參閱 Instruments 27 與 app 回應性。真正能修好一個 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 分鐘閱讀