What's new in Swift
Swift & UI 进阶 30m

Swift 新特性一览

What's new in Swift

2021年6月7日

在 Apple 官方观看视频

一句话判断

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 letwithCheckedThrowingContinuation 桥接现有的 completion handler 接口。先迁移独立的网络层和数据层,再逐步向上扩散。Actor 可以从新的共享状态类开始引入,旧的单线程类不需要急着改成 Actor。

还有什么值得关注

  • async let 允许你并行发起多个异步操作,语法比 Task Group 更轻量。
  • Swift Package Manager 的命令插件(Command Plugin)可以执行代码格式化、生成文档等自定义构建步骤。
  • Swift 5.5 的 Codable 改进了对默认值的支持,减少了 Boilerplate 代码。
WWDC 2021