Swift 新特性一览
What's new in Swift
2021年6月7日
一句话判断
Swift 5.5 最大的变化是 async/await 和 Actor 的引入,这是 Swift 并发模型的一次彻底重构,影响范围远超语言本身。
这场 Session 讲了什么
Session 全面介绍了 Swift 5.5 的语言特性更新。重头戏显然是结构化并发(Structured Concurrency)——async/await、Actor、async sequence 和 Task 的组合拳。这套方案不只是在语法层面加了两个关键字,而是重新定义了 Swift 中并发代码的编写、组织和调试方式。
除了并发,还介绍了其他实用特性:#if 后面的表达式现在支持更复杂的条件判断;属性包装器(Property Wrapper)可以用在函数和闭包参数上;Swift Package Manager 支持了包级别的自定义构建插件和命令插件。这些小特性单独看不起眼,但组合起来对日常开发效率的提升是实打实的。
Session 还回顾了 Swift 在过去一年中的增长数据——超过一半的 Swift 开发者在 Apple 生态之外使用 Swift,这对语言的演进方向有深远影响。
值得深挖的点
async/await 不是语法糖
把 completion handler 改成 async/await 表面上只是写法变化,但底层语义完全不同。async 函数在_suspend point_处可以被挂起,此时线程被释放给系统调度器去做别的事情。这意味着你不再需要自己管理线程池,系统会替你做。但这也带来了新的心智模型:async 函数调用之间的状态不是你想象的那么连续,你需要重新思考数据竞争(Data Race)的问题。这就是 Actor 存在的原因——它通过隔离(Isolation)机制保证内部状态不会被并发访问。
Actor 的隔离模型
Actor 是一种引用类型,但它的所有内部状态默认被隔离在 actor 的执行上下文中。外部代码不能直接访问 actor 的属性,必须通过 await 调用 actor 的方法。这个设计选择非常激进:它把并发安全从”靠纪律”变成了”靠编译器”。代价是迁移成本——你需要显式标注哪些代码运行在哪个 actor 上,@MainActor 标注会在未来成为 iOS 开发中的高频操作。
代码片段
async/await 基础用法
// 从网络加载图片, async/await 让异步代码线性化
func loadImage(from url: URL) async throws -> UIImage {
let (data, _) = try await URLSession.shared.data(from: url)
guard let image = UIImage(data: data) else {
throw ImageError.invalidData
}
return image
}
// 在 UI 中调用
Task { @MainActor in
imageView.image = try await loadImage(from: imageURL)
}
Actor 保护共享状态
actor ImageCache {
private var cache: [URL: UIImage] = [:]
func image(for url: URL) -> UIImage? {
cache[url]
}
func setImage(_ image: UIImage, for url: URL) {
cache[url] = image
}
}
// 外部必须通过 await 访问
let cached = await cache.image(for: url)
属性包装器用在函数参数上
// 用 @Clamping 限制输入范围
@propertyWrapper
struct Clamping<Value: Comparable> {
var wrappedValue: Value
let range: ClosedRange<Value>
init(wrappedValue: Value, _ range: ClosedRange<Value>) {
self.range = range
self.wrappedValue = min(max(wrappedValue, range.lowerBound), range.upperBound)
}
}
func setProgress(@Clamping(0...1) progress: Double) {
// progress 被自动钳位到 0...1
}
最佳实践
新项目: 从一开始就使用 async/await 作为异步编程模型。网络请求、文件 I/O、耗时计算全部用 async 函数。共享可变状态用 Actor 保护。在视图层用 @MainActor 标注 UI 更新代码。
已有项目: 不要一口气全量迁移。先用 async let 和 withCheckedThrowingContinuation 桥接现有的 completion handler 接口。先迁移独立的网络层和数据层,再逐步向上扩散。Actor 可以从新的共享状态类开始引入,旧的单线程类不需要急着改成 Actor。
还有什么值得关注
async let允许你并行发起多个异步操作,语法比 Task Group 更轻量。- Swift Package Manager 的命令插件(Command Plugin)可以执行代码格式化、生成文档等自定义构建步骤。
- Swift 5.5 的
Codable改进了对默认值的支持,减少了 Boilerplate 代码。