`@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 并发的快车道------既不掉链,也不甩锅。

相关推荐
Haha_bj1 天前
Swift——高阶函数(map、filter、reduce、forEach、sorted、contains……)
ios·app·swift
妮妮分享1 天前
维智地图如何集成
开发语言·ios·swift
汉秋2 天前
告别 GeometryReader:SwiftUI .visualEffect 实战解析
swiftui·swift
linweidong2 天前
美团ios开发100道面试题及参考答案(下)
objective-c·swift·jspatch·ios开发·ios面试·ios面经·xcode调试
__WanG3 天前
screen time api - FamilyActivityPicker 获取选中应用
ios·iphone·swift
东坡肘子4 天前
Swift、SwiftUI 与 SwiftData:走向成熟的 2025 -- 肘子的 Swift 周报 #116
人工智能·swiftui·swift
大熊猫侯佩5 天前
Swift 6.2 列传(第十三篇):香香公主的“倾城之恋”与优先级飞升
swift·编程语言·apple
1024小神5 天前
Swift配置WKwebview加载网站或静态资源后,开启调试在电脑上debug
swift
kkoral6 天前
基于MS-Swift 为 Qwen3-0.6B-Base 模型搭建可直接调用的 API 服务
python·conda·fastapi·swift
Yorelee.7 天前
ms-swift在训练时遇到的部分问题及解决方案
开发语言·nlp·transformer·swift