01:03:49
随着 Swift 6 的发布,数据竞争安全(Data Race Safety)成为开发者关注的焦点。Swift 的并发模型通过编译时静态检查,从根源上杜绝难以调试的并发问题。然而,在将现有代码迁移至新模型时,许多团队面临理解误区与技术选型困惑。本次特别邀请了 Apple 核心工程师团队——Holly(Swift 编译器)、Jeremy(Foundation 框架)、Seema(SwiftUI)以及 Alan(Swift 编译器)——针对开发者高频问题展开深度解答。以下是核心要点整理。
Holly 指出,许多开发者期望一步到位,同时完成「使用 async/await 等现代特性」和「开启 Swift 6 严格并发检查」,这往往导致大量诊断信息令人无所适从。实际上,这是两个可独立推进的步骤:
两者顺序无需固定,关键在于迭代。Jeremy 分享 Foundation 的迁移经验时提到,团队先开启诊断,再逐步为类型添加 Sendable 标注或 @MainActor,许多警告会成片消失。Seema 则建议先开启 Swift 5 下的严格并发检查(Strict Concurrency Checking),以预览问题全貌,再用 @nonisolated(unsafe) 等临时 escape hatch 渐进式推进。
🔹 @MainActor.run vs. Task { @MainActor in }
如果希望整个任务体都运行在主线程,使用 Task { @MainActor in ... };若只想让任务内的某几行代码跑在主线程,推荐 MainActor.run 或将那部分逻辑抽成独立的 @MainActor 函数。前者是整个闭包的隔离域切换,后者是点状切换。
🔹 在 @MainActor 类型中执行耗时操作
若视图模型(ViewModel)标记为 @MainActor,但某个方法包含大量 I/O 或计算,可直接将该方法标注为 @concurrent(或使用 @nonisolated async 搭配 Upcoming Feature)。编译器会阻止该方法直接访问同一类型的可变状态,从而保证线程安全。
🔹 Task 会阻塞主线程吗?
Task 的启动本身不会阻塞主线程,因为异步任务在遇到挂起点(suspension point)时会让出执行资源。但若在 @MainActor 上执行大量同步耗时工作,仍可能卡住 UI。建议先利用 Instruments 分析性能瓶颈,再有针对性地将重活移到后台。
🔹 取消 Task 的最佳实践
保存任务句柄(Task.Handle),并在合适的时机(如视图 disappears 或类型析构)调用 task.cancel()。但取消是协作式的,任务内的操作必须检查 try Task.checkCancellation() 才能响应。另外,SwiftUI 的 .task 修饰符会在视图生命期结束时自动取消关联的任务。
Swift 通过隔离域(isolation domain)将可变状态保护起来。Alan 解释,nonisolated 表示声明不偏好任何特定隔离域,可自由地在任何上下文中调用。而 @concurrent 则是显式要求异步函数必须派发到全局共享线程池,它替代了早期 async 函数隐式跳到后台的行为(即 nonisolated async 在 Upcoming Features 启用后的新语义)。
关于何时使用 Actor,工程师一致强调:尽量少用 Actor。每个 Actor 都是一个独立隔离域,跨域访问必须 await,这会增加程序复杂度。只有当某个类型确实拥有需要被串行访问的可变状态(如网络缓存、数据库连接)时,才值得引入 Actor。大多数同步代码应保持同步,除非有明确的并发需求。
关键决策参考:如果异步函数需要保证在后台并行运行,使用 @concurrent;如果只想声明该函数不绑定特定线程(并可被覆盖调用方的隔离),使用 nonisolated。在 Swift 6 中,默认的 async 函数已不再自动跳转到后台,除非显式标注 @concurrent。
Jeremy 详细介绍了 Foundation 框架的迁移哲学:增量式、靶向优先。先找出容易标注(如给纯值类型添加 Sendable,将 UI 相关类标记 @MainActor)的代码,通过开启 Upcoming Features 逐类诊断验证,再逐步解决更复杂的部分(如重新设计 NotificationCenter 的异步 API)。
Holly 补充,对于旧的不常修改的代码,不必强行重写;优先确保新代码以符合并发模型的方式编写,旧代码可随时间在有实际需求时再触碰。使用 @preconcurrency 或 @nonisolated(unsafe) 可以暂时桥接新旧两种模式。
针对大量文件并行处理场景,不要理论化方案,先使用最直接的创建多个 Task 的方式,通过 Instruments 分析后再决定是否引入批处理或任务组(TaskGroup)。
回答中透露了多个即将在 Swift 6.4 中实装的提案:
Seema 建议通过 Swift 导师计划(Mentorship Program)开始为开源项目做贡献,除提交代码外,对并发诊断提出疑问或参与论坛讨论也是极具价值的贡献方式。Holly 强调,许多编译器诊断改善正是源于社区的反馈细节。
Swift 并发模型的核心优势在于显式、可推导、静态安全。通过将迁移拆解为可管理的步骤,充分利用编译器提供的临时 escape hatch 与逐步开启的 Upcoming Features,任何规模的项目都能平稳过渡到 Swift 6 的并发世界观。后续实践中遇到的具体问题,建议结合 Instruments 分析,并关注 Swift Evolution 的最新动态。