From acfdaa1f4f15e9c6d785fa2276a5a0fff105d466 Mon Sep 17 00:00:00 2001 From: link2026 Date: Mon, 25 May 2026 17:00:30 +0800 Subject: [PATCH] =?UTF-8?q?fix(concurrency):=20nonisolated(unsafe)=20stati?= =?UTF-8?q?c=20shared=20+=20=E4=BF=AE=E5=90=8C=20actor=20=E5=86=85?= =?UTF-8?q?=E5=86=97=E4=BD=99=20await?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 项目开启了 -default-isolation=MainActor upcoming feature,导致: 1. static let shared 默认被视为 MainActor 隔离,即使 class 标了 @unchecked Sendable,从其他 actor(如 AIRuntime)同步访问仍报 "Expression is 'async' but is not marked with 'await'". 修法:ModelStore.shared 和 FileVault.shared 都加 nonisolated(unsafe) 修饰,明确"任何隔离上下文都可同步访问"。 2. AIRuntime.generate() 内的 Task { ... } 继承 AIRuntime actor 隔离, self.recordRate 是同 actor 内部调用,不需要 await,否则报 "No 'async' operations occur within 'await' expression". 修法:去掉冗余的 await。 ** BUILD SUCCEEDED ** 已验证。 Co-Authored-By: Claude Opus 4.7 (1M context) --- 体己/AI/AIRuntime.swift | 4 +++- 体己/AI/ModelStore.swift | 4 +++- 体己/Persistence/FileVault.swift | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/体己/AI/AIRuntime.swift b/体己/AI/AIRuntime.swift index cdc518a..e061c0f 100644 --- a/体己/AI/AIRuntime.swift +++ b/体己/AI/AIRuntime.swift @@ -80,7 +80,9 @@ actor AIRuntime { // session.generate 跨 actor 边界,需要 await let stream = await session.generate(prompt: prompt, maxTokens: maxTokens) for try await chunk in stream { - await self.recordRate(chunk.decodeRate) + // Task 闭包在 generate() 内启动,继承 AIRuntime 的 actor 隔离; + // 调用同 actor 的 recordRate 不需要 await + self.recordRate(chunk.decodeRate) continuation.yield(chunk) } continuation.finish() diff --git a/体己/AI/ModelStore.swift b/体己/AI/ModelStore.swift index 06ce918..834db85 100644 --- a/体己/AI/ModelStore.swift +++ b/体己/AI/ModelStore.swift @@ -21,8 +21,10 @@ enum ModelKind: String, CaseIterable { /// `@unchecked Sendable`:rootURL 是 let,方法只读 filesystem(线程安全), /// 可被任意 actor / Task 跨边界访问。 +/// `nonisolated(unsafe) shared`:项目开启 `-default-isolation=MainActor` 后 +/// static 默认 MainActor 隔离,跨 actor 访问需要 await。这里手动 opt-out。 final class ModelStore: @unchecked Sendable { - static let shared: ModelStore = { + nonisolated(unsafe) static let shared: ModelStore = { do { let appSupport = try FileManager.default.url( for: .applicationSupportDirectory, diff --git a/体己/Persistence/FileVault.swift b/体己/Persistence/FileVault.swift index 0d42634..77c4dc8 100644 --- a/体己/Persistence/FileVault.swift +++ b/体己/Persistence/FileVault.swift @@ -10,8 +10,9 @@ enum FileVaultError: Error { /// `@unchecked Sendable`:rootURL 是 let,方法只 I/O 到沙盒目录(线程安全), /// 可被任意 actor / Task 跨边界访问。 +/// `nonisolated(unsafe) shared`:见 ModelStore 同款注释。 final class FileVault: @unchecked Sendable { - static let shared: FileVault = { + nonisolated(unsafe) static let shared: FileVault = { do { let appSupport = try FileManager.default.url( for: .applicationSupportDirectory,