--- name: swift-actor-persistence description: 在 Swift 中使用 Actors 進行線程安全的資料持久化 — 結合記憶體快取與檔案儲存,從設計上消除資料競爭 (Data Races)。 --- # 使用 Swift Actors 達成線程安全持久化 這是在 Swift 中使用 Actor 構建線程安全資料持久層的模式。它結合了記憶體快取 (In-memory cache) 與磁碟檔案存儲,利用 Actor 模型在編譯時期即消除資料競爭風險。 ## 何時啟用 - 在 Swift 5.5+ 環境下構建資料持久層。 - 對共享的可變狀態 (Shared Mutable State) 需要線程安全存取。 - 希望淘汰手動同步機制(如 Locks, DispatchQueue)。 - 開發具備本地存儲能力的離線優先 (Offline-first) 應用程式。 ## 核心模式:基於 Actor 的 Repository Actor 模型保證了序列化存取 — 由編譯器強制執行單一時間點僅有一個任務能存取其內部狀態。 ```swift public actor LocalRepository where T.ID == String { private var cache: [String: T] = [:] private let fileURL: URL public init(directory: URL = .documentsDirectory, filename: String = "data.json") { self.fileURL = directory.appendingPathComponent(filename) // 在 init 期間同步載入(此時 Actor Isolation 尚未啟用) self.cache = Self.loadSynchronously(from: fileURL) } // 提供給外部使用的線程安全 API public func save(_ item: T) throws { cache[item.id] = item try persistToFile() } public func find(by id: String) -> T? { cache[id] } // 私有實作:原子化寫入檔案以防止損毀 private func persistToFile() throws { let data = try JSONEncoder().encode(Array(cache.values)) try data.write(to: fileURL, options: .atomic) } } ``` ## 使用方式 由於 Actor 隔離機制,所有外部調用皆會自動轉換為 `async` 行為: ```swift let repository = LocalRepository() // 讀取 — 從記憶體快取中進行極速 O(1) 查找 let user = await repository.find(by: "u-001") // 寫入 — 同步更新快取並原子化持久化至磁碟 try await repository.save(newUser) ``` ## 實踐之最佳實踐 - **使用 `Sendable` 類型**:確保所有跨越 Actor 邊界的資料皆符合 Sendable 協定。 - **內部實作封裝**:Actor 的公開 API 應僅暴露業務邏輯操作,隱藏持久化細節。 - **原子化寫入 (`.atomic`)**:防止 App 在寫入中途崩潰導致資料損毀。 - **配合 `@Observable`**:在 ViewModel 中使用,為 UI 提供反應式數據更新。 ## 應避免的反模式 - 在新的 Swift Concurrency 程式碼中繼續混用 `DispatchQueue` 或 `NSLock`。 - 對外暴露內部的 Cache 字典。 - 使用 `nonisolated` 關鍵字來規避 Actor 隔離(這會破壞線程安全性)。 ## 適用情境 - iOS/macOS 應用的本地資料存儲(用戶資料、偏好設定、快取內容)。 - 需要更換舊有的 `DispatchQueue` 式同步邏輯,轉向現代化 Swift Concurrency 架構。