Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Swift 6.2のアクセシブルな並行処理——デフォルトはシングルスレッド、@concurrentは明示的なバックグラウンドオフロードに使用し、分離の一貫性はMainActor型に使用する。
.claude/skills/affaan-m-swift-concurrency-6-2/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 17% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 71% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 40% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 61% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 121% | 0% |
采用 Swift 6.2 并发模型的模式,其中代码默认在单线程上运行,并发是显式引入的。在无需牺牲性能的情况下消除常见的数据竞争错误。
在 Swift 6.1 及更早版本中,异步函数可能会被隐式卸载到后台线程,即使在看似安全的代码中也会导致数据竞争错误:
swift// 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// 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 类型现在可以安全地符合非隔离协议:
swiftprotocol 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() } }
编译器确保该一致性仅在主 actor 上使用:
swift// 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// 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 — 无需手动标注:
swift// 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 上,使得可变状态的访问变得安全。如果没有这些设置,此代码存在数据竞争 — 编译器会标记它。
swiftnonisolated 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 static func extractSubject(from data: Data) async -> Sticker { /* ... */ } } // Callers must await let processor = PhotoProcessor() processedPhotos[item.id] = await processor.extractSticker(data: data, with: item.id)
要使用 @concurrent:
nonisolated@concurrentasyncawait| 决策 | 原理 | |----------|-----------| | 默认单线程 | 最自然的代码是无数据竞争的;并发是选择启用的 | | 异步函数保持在调用者所在的 actor 上 | 消除了导致数据竞争错误的隐式卸载 | | 隔离的一致性 | MainActor 类型可以符合协议,而无需不安全的变通方法 | | @concurrent 显式选择启用 | 后台执行是一种有意的性能选择,而非偶然 | | MainActor 默认推断 | 减少了应用目标中样板化的 @MainActor 标注 | | 选择启用采用 | 非破坏性的迁移路径 — 逐步启用功能 |
SwiftSettings API@concurrent:先进行性能分析,然后卸载热点路径@concurrent — 图像处理、压缩、复杂计算nonisolated 变通方法或 @Sendable 包装器@concurrent(大多数不需要后台执行)nonisolated 来抑制编译器错误DispatchQueue 模式model.availability 检查| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 19,280 | 21,802 | +13% | 1 | 1 | 0% | 3,500 | 4,102 | +17% | 0 | 0 | — |
case-02 | fail→pass | 13,734 | 10,787 | -21% | 1 | 1 | 0% | 2,185 | 3,737 | +71% | 0 | 0 | — |
case-03 | fail→pass | 16,280 | 10,192 | -37% | 1 | 1 | 0% | 2,761 | 3,868 | +40% | 0 | 0 | — |
case-04 | pass→pass | 15,867 | 11,078 | -30% | 1 | 1 | 0% | 2,555 | 3,871 | +52% | 0 | 0 | — |
case-05 | pass→pass | 27,558 | 7,616 | -72% | 1 | 1 | 0% | 1,715 | 3,121 | +82% | 0 | 0 | — |
case-06 | pass→pass | 12,355 | 10,826 | -12% | 1 | 1 | 0% | 1,978 | 3,654 | +85% | 0 | 0 | — |
case-07 | pass→pass | 6,299 | 4,761 | -24% | 1 | 1 | 0% | 992 | 2,785 | +181% | 0 | 0 | — |
case-16 | pass→pass | 18,650 | 12,752 | -32% | 1 | 1 | 0% | 2,934 | 4,076 | +39% | 0 | 0 | — |
case-08 | pass→pass | 8,012 | 7,857 | -2% | 1 | 1 | 0% | 1,326 | 3,287 | +148% | 0 | 0 | — |
case-09 | fail→pass | 16,241 | 13,755 | -15% | 1 | 1 | 0% | 2,610 | 4,201 | +61% | 0 | 0 | — |
case-10 | pass→pass | 16,801 | 11,348 | -32% | 1 | 1 | 0% | 2,874 | 3,900 | +36% | 0 | 0 | — |
case-11 | fail→pass | 9,881 | 10,542 | +7% | 1 | 1 | 0% | 1,799 | 3,968 | +121% | 0 | 0 | — |
case-12 | fail→pass | 17,393 | 12,336 | -29% | 1 | 1 | 0% | 2,592 | 4,130 | +59% | 0 | 0 | — |
case-13 | fail→pass | 13,584 | 9,113 | -33% | 1 | 1 | 0% | 2,175 | 3,478 | +60% | 0 | 0 | — |
case-14 | pass→pass | 11,329 | 6,965 | -39% | 1 | 1 | 0% | 2,072 | 3,122 | +51% | 0 | 0 | — |
case-15 | fail→pass | 5,654 | 2,180 | -61% | 1 | 1 | 0% | 924 | 2,275 | +146% | 0 | 0 | — |
case-17 | pass→pass | 6,413 | 3,426 | -47% | 1 | 1 | 0% | 943 | 2,459 | +161% | 0 | 0 | — |
case-18 | pass→pass | 10,629 | 8,076 | -24% | 1 | 1 | 0% | 1,887 | 3,273 | +73% | 0 | 0 | — |
case-19 | fail→pass | 18,567 | 15,329 | -17% | 1 | 1 | 0% | 2,260 | 3,792 | +68% | 0 | 0 | — |
case-20 | fail→pass | 15,459 | 7,305 | -53% | 1 | 1 | 0% | 2,217 | 3,092 | +39% | 0 | 0 | — |
case-21 | fail→pass | 38,907 | 26,940 | -31% | 1 | 1 | 0% | 3,781 | 4,398 | +16% | 0 | 0 | — |
case-22 | pass→pass | 11,579 | 11,402 | -2% | 1 | 1 | 0% | 1,829 | 3,837 | +110% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +50 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.