帮助开发者理解 Swift 6.2 并发模型与主线程隔离的最佳实践。
复制安装指令,让 AI 自动完成配置 · 推荐新手
请帮我安装 askskill 上的 "swift-concurrency-6-2" 技能: 1. 下载 https://raw.githubusercontent.com/affaan-m/ECC/main/docs/ja-JP/skills/swift-concurrency-6-2/SKILL.md 2. 保存为 ~/.claude/skills/swift-concurrency-6-2/SKILL.md 3. 装好后重载技能,告诉我可以用了
请用简明示例解释 Swift 6.2 的并发设计:为什么默认单线程更安全,@concurrent 应该在什么场景使用,以及 MainActor 类型的一致隔离是什么意思。
一份面向开发者的说明,包含核心概念、适用场景和简短代码示例。
我有一段 Swift 异步代码,请帮我按 Swift 6.2 的并发建议重构:默认保持单线程执行,只把耗时任务显式迁移到后台,并检查是否需要使用 MainActor 隔离。请给出修改前后对比与原因。
重构后的 Swift 代码,附带并发风险分析和修改理由。
请审查这段涉及 SwiftUI 或 UIKit 的代码,判断哪些状态更新必须放在 MainActor 上,哪些后台任务适合用 @concurrent 处理,并指出潜在的数据竞争问题。
一份线程安全审查结果,列出问题点、修复建议和推荐注解。
コードがデフォルトでシングルスレッドで実行され、並行処理が明示的に導入されるSwift 6.2の並行処理モデルを採用したパターン。パフォーマンスを犠牲にすることなく、よくあるデータ競合エラーを排除する。
Swift 6.1以前では、非同期関数が暗黙的にバックグラウンドスレッドにオフロードされ、一見安全に見えるコードでもデータ競合エラーを引き起こすことがあった:
// Swift 6.1: ERROR
@MainActor
final class StickerModel {
let photoProcessor = PhotoProcessor()
func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? {
guard let data = try await item.loadTransferable(type: Data.self) else { return nil }
// Error: Sending 'self.photoProcessor' risks causing data races
return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier)
}
}
Swift 6.2ではこの問題が修正された:非同期関数はデフォルトで呼び出し元と同じActorに留まる。
// Swift 6.2: OK — async stays on MainActor, no data race
@MainActor
final class StickerModel {
let photoProcessor = PhotoProcessor()
func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? {
guard let data = try await item.loadTransferable(type: Data.self) else { return nil }
return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier)
}
}
MainActor型が非分離プロトコルに安全に準拠できるようになった:
protocol Exportable {
func export()
}
// Swift 6.1: ERROR — crosses into main actor-isolated code
// Swift 6.2: OK with isolated conformance
extension StickerModel: @MainActor Exportable {
func export() {
photoProcessor.exportAsPNG()
}
}
コンパイラはこの一貫性がMainActor上でのみ使用されることを保証する:
// OK — ImageExporter is also @MainActor
@MainActor
struct ImageExporter {
var items: [any Exportable]
mutating func add(_ item: StickerModel) {
items.append(item) // Safe: same actor isolation
}
}
// ERROR — nonisolated context can't use MainActor conformance
nonisolated struct ImageExporter {
var items: [any Exportable]
mutating func add(_ item: StickerModel) {
items.append(item) // Error: Main actor-isolated conformance cannot be used here
}
}
MainActorを使用してグローバル/静的状態を保護する:
// Swift 6.1: ERROR — non-Sendable type may have shared mutable state
final class StickerLibrary {
static let shared: StickerLibrary = .init() // Error
}
// Fix: Annotate with @MainActor
@MainActor
final class StickerLibrary {
static let shared: StickerLibrary = .init() // OK
}
Swift 6.2ではMainActorをデフォルトで推論するパターンが導入された——手動の注釈なし:
// With MainActor default inference enabled:
final class StickerLibrary {
static let shared: StickerLibrary = .init() // Implicitly @MainActor
}
final class StickerModel {
let photoProcessor: PhotoProcessor
var selection: [PhotosPickerItem] // Implicitly @MainActor
}
extension StickerModel: Exportable { // Implicitly @MainActor conformance
func export() {
photoProcessor.exportAsPNG()
}
}
このパターンはオプトインで、アプリ、スクリプト、その他の実行可能ターゲットに推奨される。
真の並列処理が必要な場合、@concurrent を使って明示的にオフロードする:
重要: この例は「アクセシブルな並行処理」ビルド設定——SE-0466 (MainActorデフォルト分離) と SE-0461 (デフォルト非分離非送信) の有効化が必要。これらの設定を有効にすると、
extractStickerは呼び出し元のActorに留まり、可変状態へのアクセスが安全になる。これらの設定なしでは、このコードにはデータ競合がある——コンパイラがフラグを立てる。
nonisolated final class PhotoProcessor {
private var cachedStickers: [String: Sticker] = [:]
func extractSticker(data: Data, with id: String) async -> Sticker {
if let sticker = cachedStickers[id] {
return sticker
}
let sticker = await Self.extractSubject(from: data)
cachedStickers[id] = sticker
return sticker
}
// Offload expensive work to concurrent thread pool
@concurrent
…