3.0 KiB
3.0 KiB
| name | description |
|---|---|
| swift-actor-persistence | 在 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 模型保證了序列化存取 — 由編譯器強制執行單一時間點僅有一個任務能存取其內部狀態。
public actor LocalRepository<T: Codable & Identifiable> 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 行為:
let repository = LocalRepository<User>()
// 讀取 — 從記憶體快取中進行極速 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 架構。