在開發 SwiftUI App 的歷程中,我們都曾被那些難以捉摸、被稱為「Heisenbugs」的數據競爭(Data-race bugs)困擾過。或許是 App 狀態在某些邊界條件下產生不尋常的跳動,或是動畫發生突兀的閃爍,最糟的情況甚至是導致數據永久遺失或閃退。
然而,透過 WWDC25 的技術揭露,我們看到 Swift 與 SwiftUI 正在讓這些開發噩夢成為歷史。作為一名長期與 Apple 平台打交道的開發者,我觀察到這不只是工具的升級,更是開發思維的典範轉移。以下是我整理出關於 SwiftUI 並行處理的 5 個關鍵發現,它們將徹底改變你構建 App 的方式。
@MainActor 是你的預設「安全區」
在以往,我們常在代碼中頻繁標註 @MainActor。現在,Swift 透過「推斷隔離 Inferred Isolation」機制,讓這一切在編譯時自動完成。
編譯器比你更了解隔離需求
當你的結構體符合 View 協議時,它就自動獲得了 @MainActor 隔離。這意味著視圖屬性(如 @State)與 body 內的邏輯都預設在主執行緒上執行。更震撼的是,這種隔離延伸到了與舊框架的互操作性:
- UIViewRepresentable 的自動繼承:由於它精煉(refine)了
View協議,它同樣擁有主執行緒隔離。這意味著你在makeUIView中初始化UILabel時,無需手動添加標註 - 模型實例的隱式保護:如果你在視圖聲明中直接初始化數據模型實例(例如
ColorExtractorView內的 Model),Swift 會確保該實例同樣受到適當的隔離保護
SwiftUI 將主執行緒(Main Actor)視為應用程式在編譯時與執行時的預設值。
背景執行緒的高頻率幾何計算
儘管視圖主要在主執行緒運行,但 SwiftUI 為了實現「絲滑順暢 Buttery smooth」的用戶體驗,會在幕後自動將重型計算轉移到背景執行緒。 UI 代碼在背景運行的「悖論」 這看似反直覺,但卻是為了釋放主執行緒以響應使用者操作。這類優化通常發生在高頻率幾何計算(High-frequency geometry calculations)的場景:
- Shape 協議:當你在
path(in:)中計算複雜的動畫路徑時 - visualEffect 與 onGeometryChange:這些 API 的閉包可能在背景執行緒被調用,以處理昂貴的視覺特效運算
- Layout 協議:執行複雜的自定義佈局邏輯時
這種設計讓我們能在不犧牲互動反應速度的情況下,實現極其複雜的動畫與視覺效果。
學會「傳送 Sending」資料與 Sendable 救命繩
當編譯器報出 Sendable 警告時,它不是在限制你,而是在引導你正確地跨越隔離邊界。
理解「傳送」的運作機制
在 visualEffect 等非隔離閉包中存取主執行緒屬性時,衝突便會產生。這裡涉及一個核心概念:傳送(Sending)。
- 視圖是 Sendable 的:因為
View受到@MainActor保護,Swift 允許將self(即視圖本身)傳送到背景執行緒 - 屬性存取受阻:雖然可以「傳送」視圖,但編譯器不允許在非隔離區域讀取視圖內受隔離的屬性(如
pulse)
最優策略:不共享,只複製
解決之道不是去「保護」共享數據,而是根本不要共享。透過捕獲列表(Capture List)製作變數副本(例如 [pulse = self.pulse]),你傳送的是一個獨立的值類型(如 Bool),這徹底消除了數據競爭的可能性。
為什麼 Button 的 Action 必須是同步的?
許多開發者疑惑為何 Button 的回調不直接支援 async?答案藏在硬體物理限制-設備刷新率(Refresh Rate)。
避開「暫停點 Suspension Points」的陷阱
UI 框架必須滿足螢幕刷新的嚴苛截止時間。當你在異步函數中使用 await 時,會產生暫停點。此時 Swift 運行時可能會暫停該任務去處理其他事務,暫停的時間長度是不可控的。
同步變更對於創造流暢的互動至關重要。UI 框架必須面對設備要求特定屏幕刷新率的現實。
如果 UI 變更是異步的,延遲的恢復執行會導致動畫掉幀。因此,在同步回調中立即觸發狀態變更(例如設定 isLoading = true)是確保操作感即時且精準的唯一途徑。
建立 UI 與非同步邏輯的清晰邊界
為了構建強健的系統,我們需要學會如何在視圖與非同步任務之間劃定界限。
以 State 作為通訊橋樑
建議將非同步工作從視圖邏輯中抽離。視圖應保持同步且靈敏,而異步任務獨立運作,並以 State 作為橋樑:
- UI 觸發:同步回調啟動異步任務
- 狀態更新:當異步任務完成後,進行同步的狀態變更
- 單元測試的紅利:這種架構能讓你輕鬆地為異步邏輯編寫測試,甚至不需要
import SwiftUI,實現邏輯與界面的徹底解耦
前瞻性總結
Swift 6.2 帶來的 @MainActor 簡化,標誌著並行處理已從「手動防禦」演進為「語言層級的安全性」。我建議你現在就嘗試在專案中開啟新的語言模式,你會驚訝地發現可以移除掉多少冗餘的註解。
給開發者的進階挑戰:
- 嘗試 Mutex:它是將類別(Class)安全地標註為
Sendable的重要工具,建議閱讀其官方文檔 - 編寫脫離 SwiftUI 的單元測試:挑戰自己在不導入 UI 框架的情況下,驗證你的異步業務邏輯
當並行處理變得如此安全且自動化時,數據競爭已成往事。現在,你可以全神貫注地去構建那些曾經因擔心執行緒安全而不敢嘗試的複雜體驗了。你的下一個 App,將會有多流暢?
關於 XcodeProject
XcodeProject 創立於 2023,致力於協助開發者探索 Apple 的創新世界,學習在 iOS、iPadOS、macOS、tvOS、visionOS 與 watchOS 上開發 App,發現眾多技術與框架,讓開發者獲得更多能力。