Swift 的新变化
What's new in Swift
2024年6月10日
一句话判断
Swift 6 语言模式带来了编译期的 data race 安全检查——如果你写并发代码,这是今年最重要的更新,但迁移策略一定要”渐进式”而不是”一步到位”。
这场 Session 讲了什么
2024 年是 Swift 发布十周年。Session 回顾了 Swift 的演进历程(从 Swift 3 的痛苦迁移到 Swift 5 的稳定 ABI,再到 async/await、C++ 互操作、Macros),然后重点介绍了 Swift 6 的核心变化。
Swift 6 的主题是三个方向:可移植性(扩展到 Fedora、Debian 等新平台,引入 fully static Linux SDK 支持从 macOS 交叉编译到 Linux)、性能(编译器优化和运行时改进)、以及开发者体验(Swift 6 语言模式提供编译期 data race 安全保证)。Session 用大量代码示例展示了如何逐步迁移到 Swift 6 语言模式——核心策略是使用 nonisolated(nonsending) 注解来标注不需要发送隔离的类型,以及如何处理常见的并发迁移问题。
值得深挖的点
Swift 6 语言模式与 Data Race 安全
Swift 6 语言模式最核心的变化是在编译期检测 data race。在 Swift 6 下,编译器会检查所有跨并发域的值传递,确保不会出现两个线程同时修改同一块内存的情况。这不是运行时检查,而是编译期静态分析。
迁移策略继承自 Swift 4 的经验——同一个编译器支持多种语言模式,你可以逐个模块迁移。通过 SWIFT_UPCOMING_FEATURE_EXPLICIT_SENDABLE 等编译标志逐步启用新检查,而不是一次性把整个项目切成 Swift 6 模式。
最实用的迁移工具是 nonisolated(nonsending) 注解。很多类型被用于单个并发域内,不需要跨域传递——给它们加上这个注解,编译器就不会要求它们遵循 Sendable 协议。这大幅减少了迁移时需要处理的类型数量。
Fully Static Linux SDK 与交叉编译
Apple 引入了 fully static Linux SDK,让你能在 macOS 上构建完全静态链接的 Linux 可执行文件。这意味着编译产物不需要目标 Linux 机器上安装任何额外的库。你可以在熟悉的 macOS 开发环境中开发、测试,然后交叉编译出 Linux 二进制文件,直接部署到服务器或容器中。
这个能力的底层是 Swift 的交叉编译架构。Apple 平台开发者其实一直在用交叉编译(在 Mac 上编译 iOS 应用),现在这个能力扩展到了 Linux 目标平台。配合 SourceKit-LSP,你可以在 VSCode、Neovim、Emacs 等编辑器中获得完整的 Swift 开发体验。
代码片段
使用 nonisolated(nonsending) 降低迁移成本
场景:类型只在单个并发域内使用,不需要 Sendable 检查。
// Swift 6 模式下,跨 actor 传递的类型必须遵循 Sendable
// 但很多 UI 模型只在 MainActor 上使用
@MainActor
class ViewModel {
var data: [String] = []
func update() {
// 这个方法只在 MainActor 上调用
}
}
// 如果需要暂时跳过 Sendable 检查
nonisolated(nonsending)
struct LocalConfig {
var threshold: Int
// 这个类型被标记为不需要跨域传递
// 编译器不会要求它遵循 Sendable
}
坑:nonisolated(nonsending) 是迁移过渡方案,不是长久之计。新代码应该正确遵循 Sendable 语义。
交叉编译到 Linux
场景:在 macOS 上构建完全静态链接的 Linux 服务。
# 1. 安装 static Linux SDK
# 从 swift.org 下载并安装
# 2. 构建静态 Linux 二进制
swift build --swift-sdk x86_64-swift-linux-musl
# 3. 部署到 Linux 服务器(无需安装任何依赖)
scp .build/debug/MyServer user@linux-host:~/
ssh user@linux-host '~/MyServer'
坑:静态链接的二进制文件体积会更大,但省去了目标机器上的依赖管理麻烦。
Pack Iteration
场景:Swift 6 新增的 pack iteration,简化参数包的操作。
// 以前:为 tuple 实现 == 需要手动处理每个元素
// Swift 6:用 pack iteration 自动处理任意长度
func == <each Element: Equatable>(
lhs: (repeat each Element),
rhs: (repeat each Element)
) -> Bool {
for (left, right) in repeat zip(each lhs, each rhs) {
if left != right { return false }
}
return true
}
坑:Pack iteration 是 Swift 6 的新特性,需要 Swift 6 语言模式或对应的 feature flag。
最佳实践
已有项目:不要急于切换到 Swift 6 语言模式。先在 Build Settings 中逐步启用 individual concurrency checks(如 SWIFT_STRICT_CONCURRENCY 设为 minimal 或 targeted),修复编译器报告的问题。每修好一批,再提高严格级别。nonisolated(nonsending) 可以帮你快速处理那些暂时不想改的类型。
新项目:强烈建议直接使用 Swift 6 语言模式。并发安全从第一天就是编译期保证,比事后补救便宜得多。如果你的项目需要服务端部署,考虑使用 static Linux SDK 做交叉编译——开发在 Mac,部署到 Linux,全程一份代码。
还有什么值得关注
- Swift 社区治理结构在扩展:新增 Platform Steering Group(平台指导组),正在筹建 Ecosystem Steering Group 和 Embedded Workgroup。
- swift.org/packages 集成了 Swift Package Index,帮你快速找到和评估第三方包。
- Swift 的社区移植正在将语言带到更多平台,包括 WebAssembly。
- SourceKit-LSP 已被 VSCode、Neovim、Emacs 等编辑器广泛采用,跨平台 Swift 开发体验大幅改善。