`@preconcurrency` 完全导读:让旧代码平安驶上 Swift 并发快车道

一、为什么会出现 @preconcurrency

Swift 5.5+ 的并发模型要求:

  • 跨任务传递的类型必须Sendable
  • 访问共享状态需隔离(@MainActor / actor
  • 编译器静态检查上述规则

但现实是:

  • 公司祖传框架写于 Swift 5.0
  • 第三方库没加 @Sendable / @MainActor
  • 系统 Objective-C 头文件更老

于是 Xcode 开始疯狂报红:

css 复制代码
Type 'DataFetcher' does not conform to 'Sendable'
Call to main actor-isolated instance method in a synchronous context

@preconcurrency 就是"临时通行证":

"老代码我保证安全使用,请先让我编译通过。"

二、能贴在哪儿?一张表看全

目标 示例 效果
class/struct/enum/actor @preconcurrency class Foo 整个类型视为 Sendable
protocol @preconcurrency protocol P 遵守者暂获豁免
extension @preconcurrency extension Foo 扩展内成员豁免
函数 @preconcurrency func f() 单个方法豁免
typealias @preconcurrency typealias T = Foo 别名豁免
import @preconcurrency import OldKit 整个模块一次性豁免

最常见:模块级 import 和 单个类型 声明。

三、实战:让 Swift 5.4 的老库通过 Swift 6 编译

  1. 老库源码(无法修改)
swift 复制代码
// OldKit.swift  (Swift 5.4)
public class DataFetcher {
    public func fetchData(completion: @escaping (String) -> Void) {
        DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
            completion("Legacy Data")
        }
    }
}
  1. Swift 6 客户端报红
swift 复制代码
import OldKit

Task {
    let fetcher = DataFetcher()
    fetcher.fetchData { print($0) }   // ❌ Type does not conform to Sendable
}
  1. 模块级通行证
swift 复制代码
@preconcurrency import OldKit   // ✅ 一次性静默警告

Task {
    let fetcher = DataFetcher()
    fetcher.fetchData { print($0) }   // 通过,但你负责线程安全
}

→ 整个模块所有符号暂时视为 Sendable,警告消失。

四、粒度更细:只给单类型/单函数开绿灯

若能改动源码,最小化豁免:

swift 复制代码
// 仅对单个类型
@preconcurrency
class LegacyCache {
    func save(_ data: Data) { /* 非线程安全 */ }
}

// 仅对单个函数
extension LegacyCache {
    @preconcurrency func clear() { /* 非线程安全 */ }
}

→ 调用侧同样静默,但影响面更小,便于后续逐步加固。

五、Swift 6 语言模式下的真实迁移流程

  1. 开启 -swift-version 6 → 报红爆炸
  2. 先加模块级 @preconcurrency import → 编译通过
  3. 单元测试/静态分析确保无数据竞争
  4. 逐步给类型加上 Sendable / @MainActor
  5. 删除细粒度 @preconcurrency → 完成现代化

六、安全准则:通行证 ≠ 免死金牌

✅ 安全使用 checklist

  • 类型无共享可变状态(或已加锁)
  • 函数不访问全局变量/单例
  • 回调仅在内部同步执行,不逃逸

❌ 危险示例

swift 复制代码
@preconcurrency class UnsafeCache {
    var dict: [String: Any] = [:]   // 可变 + 非 Sendable
}

→ 虽然编译通过,但多个 Task 同时写 dict 仍会数据竞争!

七、与 @Sendable 的协作关系

策略 编译通过 线程安全保证 推荐场景
@preconcurrency 人工负责 迁移过渡期
真正 Sendable 编译器检查 长期目标

口诀: "先通行证,后真护照;先上车,后补票。"

八、常见编译错误对照

错误 原因 修复
@preconcurrencycannot be applied to this declaration 贴在不被支持的声明上 改用模块级 import 或换声明类型
Type 'Foo' does not conform to 'Sendable' 未加通行证 在 import 或类型前加 @preconcurrency
Call to main actor-isolated instance method in a synchronous context 老代码在主线程外调 UI 把调用包进 MainActor.run或给方法加 @MainActor

九、一句话总结

@preconcurrency = "老代码的临时通行证",

它让 Swift 6 的严格检查暂时闭嘴, 但线程安全责任从此落到你的肩上!

记住口诀:

"模块 import 加头顶,单类型声明也 OK;先让项目跑起来,再逐步 Sendable。"

用好这张"临时驾照",让祖传代码平稳驶上 Swift 并发的快车道------既不掉链,也不甩锅。

相关推荐
HarderCoder4 小时前
Swift 中基础概念:「函数」与「方法」
swift
西西弗Sisyphus19 小时前
将用于 Swift 微调模型的 JSON Lines(JSONL)格式数据集,转换为适用于 Qwen VL 模型微调的 JSON 格式
swift·qwen3
songgeb1 天前
🧩 iOS DiffableDataSource 死锁问题记录
ios·swift
大熊猫侯佩1 天前
【大话码游之 Observation 传说】上集:月光宝盒里的计数玄机
swiftui·swift·weak·observable·self·引用循环·observations
HarderCoder1 天前
Swift 方法全解:实例方法、mutating 方法与类型方法一本通
swift
HarderCoder1 天前
Swift 类型转换实用指北:从 is / as 到 Any/AnyObject 的完整路线
swift
HarderCoder1 天前
Swift 嵌套类型:在复杂类型内部优雅地组织枚举、结构体与协议
swift
HarderCoder2 天前
Swift 枚举完全指南——从基础语法到递归枚举的渐进式学习笔记
swift
非专业程序员Ping3 天前
从0到1自定义文字排版引擎:原理篇
ios·swift·assembly·font
HarderCoder3 天前
【Swift 筑基记】把“结构体”与“类”掰开揉碎——从值类型与引用类型说起
swift