Highlight
Swift 5.5 引入了 async/await、结构化并发和 actor,并通过 Swift Collections、Swift Algorithms、Swift System、DocC 等工具补齐包生态、文档、构建性能和日常语法的开发体验。
核心内容
很多 Swift 项目的痛点不在业务代码,而在周边代码。
找一个第三方包,要先搜索网页,再复制 Git URL 到 Xcode。标准库没有双端队列、有序集合、有序字典,团队只能自己写,或者引入来源不一的实现。做系统调用时,低层接口接近 C,类型安全和跨平台体验都不够自然。
异步代码更典型。一个网络请求会拆成 dataTask、completion handler、错误参数和 resume()。真正的业务流程藏在闭包里。你想写 try/catch,却要把每个错误都塞进回调。
Swift 5.5 做了两件事。
第一,Apple 扩展了 Swift 生态。Xcode 13 支持 Swift Package Collections(包集合),Swift 官方开源包新增 Collections、Algorithms、System、Numerics 等能力。DocC 进入 Xcode,用 Markdown 注释生成框架文档。Swift Driver 成为默认编译驱动,增量导入减少了改动模块后的重编译范围。
第二,Swift 语言加入了新的并发模型。async 表示函数可以挂起,await 标记挂起点,async let 让相关任务并行运行,actor 用编译器和运行时协调共享可变状态的访问。
结果很直接。写网络请求时,代码按从上到下的顺序执行。写并行渲染时,子任务不会逃出父函数。写统计计数器时,actor 把并发访问排队,避免多个线程同时修改同一个值。
详细内容
Swift Collections:标准库之外的常用数据结构
(06:16)Swift Collections 是新的开源包,补充标准库缺少的数据结构。首版包含 Deque、OrderedSet、OrderedDictionary。
Deque 类似数组,但支持在两端高效插入和删除。
import Collections
var colors: Deque = ["red", "yellow", "blue"]
colors.prepend("green")
colors.append("orange")
// `colors` is now ["green", "red", "yellow", "blue", “orange"]
colors.popFirst() // "green"
colors.popLast() // "orange"
// `colors` is back to ["red", "yellow", "blue"]
关键点:
import Collections引入 Swift Collections 包。var colors: Deque = [...]创建一个双端队列。colors.prepend("green")在队首插入元素。colors.append("orange")在队尾插入元素。colors.popFirst()从队首删除并返回元素。colors.popLast()从队尾删除并返回元素。
(06:25)OrderedSet 同时保留顺序和唯一性。它像数组一样支持按下标访问,像集合一样保证元素只出现一次。
import Collections
var buildingMaterials: OrderedSet = ["straw", "sticks", "bricks"]
for i in 0 ..< buildingMaterials.count {
print("Little piggie #\(i) built a house of \(buildingMaterials[i])")
}
// Little piggie #0 built a house of straw
// Little piggie #1 built a house of sticks
// Little piggie #2 built a house of bricks
buildingMaterials.append("straw") // (inserted: false, index: 0)
关键点:
OrderedSet保存插入顺序。buildingMaterials.count返回元素数量。buildingMaterials[i]按顺序读取元素。append("straw")尝试插入已有元素。- 返回值里的
inserted: false表示没有插入新元素,index: 0表示已有元素位置。
(06:42)OrderedDictionary 适合键值对需要稳定顺序的场景,比如展示 HTTP 状态码列表。
import Collections
var responses: OrderedDictionary = [200: "OK", 403: "Forbidden", 404: "Not Found"]
for (code, phrase) in responses {
print("\(code) (\(phrase))")
}
// 200 (OK)
// 403 (Forbidden)
// 404 (Not Found)
关键点:
OrderedDictionary保存键值对的顺序。- 字面量
[200: "OK", ...]创建初始内容。 for (code, phrase) in responses按保存顺序遍历键和值。print输出状态码和原因短语。
Swift Algorithms 与 Swift System:把常见算法和系统调用做成 Swift API
(07:39)Swift Algorithms 是新的开源包,提供 Sequence 和 Collection 算法。演讲提到已加入 40 多个算法,包括组合、排列、分组、取最小值、取最大值、随机采样。
import Algorithms
let testAccounts = [ ... ]
for testGroup in testAccounts.uniquePermutations(ofCount: 0...) {
try validate(testGroup)
}
let randomGroup = testAccounts.randomSample(count: 5)
关键点:
import Algorithms引入 Swift Algorithms 包。testAccounts.uniquePermutations(ofCount: 0...)生成指定数量范围的唯一排列。for testGroup in ...逐个验证生成的测试组合。try validate(testGroup)表示验证过程可能抛出错误。randomSample(count: 5)从集合中随机取 5 个元素。
(07:52)Swift System 提供更符合 Swift 习惯的低层系统调用接口。它支持 Apple 平台、Linux 和 Windows。
import System
let fd: FileDescriptor = try .open(
"/tmp/a.txt", .writeOnly,
options: [.create, .truncate], permissions: .ownerReadWrite)
try fd.closeAfter {
try fd.writeAll("Hello, WWDC!\n".utf8)
}
关键点:
import System引入 Swift System 包。FileDescriptor用类型表示文件描述符。.open("/tmp/a.txt", .writeOnly, ...)以只写方式打开文件。options: [.create, .truncate]表示不存在就创建,已存在就截断。permissions: .ownerReadWrite设置所有者读写权限。fd.closeAfter { ... }在闭包执行后关闭文件描述符。fd.writeAll("Hello, WWDC!\n".utf8)写入 UTF-8 字节。
(08:06)FilePath 新增路径操作 API,可以查询和设置扩展名、添加组件、删除组件、执行路径规范化。
import System
var path: FilePath = "/tmp/WWDC2021.txt"
print(path.lastComponent) // "WWDC2021.txt"
print(path.extension) // "txt"
path.extension = "pdf" // path == "/tmp/WWDC2021.pdf"
path.extension = nil // path == "/tmp/WWDC2021"
print(path.extension) // nil
path.push("../foo/bar/./") // path == "/tmp/wwdc2021/../foo/bar/."
path.lexicallyNormalize() // path == "/tmp/foo/bar"
print(path.ends(with: "foo/bar")) // true!
关键点:
FilePath用 Swift 类型表示文件路径。path.lastComponent读取最后一个路径组件。path.extension读取扩展名。path.extension = "pdf"修改扩展名。path.extension = nil删除扩展名。path.push("../foo/bar/./")追加路径组件。path.lexicallyNormalize()规范化..和.。path.ends(with: "foo/bar")检查路径结尾。
Swift 5.5 语法改进:Codable、静态成员查找、参数属性包装器
(17:04)过去带关联值的枚举要实现 Codable,需要手写 init(from:)、encode(to:) 和多组 CodingKey。Swift 5.5 可以由编译器合成。
enum Command: Codable {
case load(key: String)
case store(key: String, value: Int)
}
关键点:
enum Command定义命令类型。: Codable声明它支持编码和解码。case load(key: String)带一个关联值。case store(key: String, value: Int)带两个关联值。- 编译器为这些 case 生成编码和解码逻辑。
(17:26)类型推断让调用枚举值时可以省略类型名。Swift 5.5 扩展了静态成员查找能力,让协议上的静态属性也能形成类似枚举的 API。
enum Coffee {
case regular
case decaf
}
func brew(_ coffee: Coffee) { ... }
brew(.regular)
关键点:
enum Coffee定义咖啡种类。case regular和case decaf是枚举成员。func brew(_ coffee: Coffee)的参数类型明确是Coffee。brew(.regular)借助类型推断省略Coffee.regular。
(18:25)属性包装器(Property Wrapper)原本常用于属性。Swift 5.5 通过 SE-0293 允许它用于函数参数和闭包参数。
@propertyWrapper
struct NonEmpty<Value: Collection> {
init(wrappedValue: Value) {
precondition(!wrappedValue.isEmpty)
self.wrappedValue = wrappedValue
}
var wrappedValue: Value {
willSet { precondition(!newValue.isEmpty) }
}
}
func logIn(@NonEmpty _ username: String) {
print("Logging in: \(username)")
}
关键点:
@propertyWrapper声明NonEmpty是属性包装器。Value: Collection限制被包装的值必须是集合类型。init(wrappedValue:)在初始化时检查集合不为空。precondition(!wrappedValue.isEmpty)在传入空值时触发运行时检查失败。wrappedValue保存真实值。willSet在修改前检查新值不为空。func logIn(@NonEmpty _ username: String)把同一套约束用在函数参数上。
SwiftUI 代码变短:绑定数组、点语法、CGFloat 转换
(19:37)演讲用一个设置列表展示 Swift 5.5 对日常 SwiftUI 代码的影响。以前要用下标访问数组里的 binding,还要显式写 CheckboxToggleStyle() 和 CGFloat(padding)。现在可以直接传入绑定数组。
// You can now write this.
import SwiftUI
struct SettingsView: View {
@State var settings: [Setting]
private let padding = 10.0
var body: some View {
List($settings) { $setting in
Toggle(setting.displayName, isOn: $setting.isOn)
#if os(macOS)
.toggleStyle(.checkbox)
#else
.toggleStyle(.switch)
#endif
}
.padding(padding)
}
}
关键点:
@State var settings: [Setting]保存可变设置数组。List($settings)把设置数组的投影绑定传给列表。{ $setting in ... }闭包直接获得每个元素的绑定。Toggle(setting.displayName, isOn: $setting.isOn)同时读取显示名并绑定开关状态。#if os(macOS)让同一段代码按平台选择样式。.toggleStyle(.checkbox)使用简化的静态成员点语法。.padding(padding)依赖编译器在Double和CGFloat之间转换。
async/await:把回调式网络请求改成顺序代码
(22:20)以前用 URLSession.dataTask 发请求,函数会先返回,真正的数据处理发生在闭包里。错误也要通过额外参数传递。
// Instead of writing this...
func fetchImage(id: String, completion: (UIImage?, Error?) -> Void) {
let request = self.imageURLRequest(for: id)
let task = URLSession.shared.dataTask(with: request) {
data, urlResponse, error in
if let error = error {
completion(nil, error)
} else if let httpResponse = urlResponse as? HTTPURLResponse,
httpResponse.statusCode != 200 {
completion(nil, MyTransferError())
} else if let data = data, let image = UIImage(data: data) {
completion(image, nil)
} else {
completion(nil, MyOtherError())
}
}
task.resume()
}
关键点:
completion: (UIImage?, Error?) -> Void用回调返回成功值或错误。URLSession.shared.dataTask(with: request)创建异步任务。- 闭包在请求完成后接收
data、urlResponse、error。 - 每个错误分支都要调用
completion(nil, ...)。 - 成功分支调用
completion(image, nil)。 task.resume()启动任务。
(24:40)Swift 5.5 的 URLSession.shared.data(for:) 可以和 try await 一起使用。函数签名声明 async throws,错误进入 Swift 原有的抛出机制。
// You can now write this.
func fetchImage(id: String) async throws -> UIImage {
let request = self.imageURLRequest(for: id)
let (data, response) = try await URLSession.shared.data(for: request)
if let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode != 200 {
throw TransferFailure()
}
guard let image = UIImage(data: data) else {
throw ImageDecodingFailure()
}
return image
}
关键点:
async throws -> UIImage表示函数会挂起,也可能抛错,成功时返回图片。let request = self.imageURLRequest(for: id)构造请求。try await URLSession.shared.data(for: request)发起请求并等待结果。let (data, response)解构返回的数据和响应元数据。HTTPURLResponse分支检查状态码。throw TransferFailure()把传输错误交给调用方处理。guard let image = UIImage(data: data)解码图片。return image返回最终结果。
结构化并发:让并行任务受父函数约束
(27:06)有些任务可以并行执行。例子里,背景图和前景图可以同时渲染,标题图单独渲染,最后合并三个结果。
func titleImage() async throws -> Image {
async let background = renderBackground()
async let foreground = renderForeground()
let title = try renderTitle()
return try await merge(background,
foreground,
title)
}
关键点:
func titleImage() async throws -> Image声明函数支持挂起和抛错。async let background = renderBackground()启动背景图渲染任务。async let foreground = renderForeground()启动前景图渲染任务。let title = try renderTitle()在当前流程中生成标题图。try await merge(...)等待并行结果就绪后合并。- 两个
async let子任务不能超出titleImage()的生命周期。 - 函数内抛错时,运行时会等待未完成任务,并向它们发出提前完成的信号。
Actor:把共享可变状态放进隔离区
(29:26)多个线程同时修改同一个计数器,结果可能损坏。Swift 5.5 的 actor 让这类状态由一个并发安全的引用类型管理。
actor Statistics {
private var counter: Int = 0
func increment() {
counter += 1
}
func publish() async {
await sendResults(counter)
}
}
var statistics = Statistics()
await statistics.increment()
关键点:
actor Statistics定义一个 actor。private var counter: Int = 0保存被保护的可变状态。func increment()在 actor 内部修改计数器。func publish() async声明发布结果时可能挂起。await sendResults(counter)等待网络等异步操作完成。var statistics = Statistics()创建 actor 实例。await statistics.increment()从 actor 外部调用方法时使用await。- actor 在可能造成数据损坏的操作前挂起调用,等安全时再执行。
核心启发
-
做什么:给图片列表做 async/await 网络加载层。 为什么值得做:
URLSession.shared.data(for:)让请求、状态码检查、图片解码按顺序写,错误走throws。 怎么开始:把旧的 completion handler 改成func fetchImage(id:) async throws -> UIImage,在视图模型里用try await调用。 -
做什么:为聊天、通知或日志页面维护有序去重列表。 为什么值得做:
OrderedSet保留顺序并保证元素唯一,适合“按到达顺序展示,但不重复”的数据。 怎么开始:引入Collections,用OrderedSet存消息 ID 或通知 ID,再用下标顺序渲染列表。 -
做什么:给测试系统生成更系统的账号组合。 为什么值得做:Swift Algorithms 提供排列、组合、随机采样等集合算法,减少手写循环。 怎么开始:引入
Algorithms,用uniquePermutations(ofCount:)枚举测试账号组合,用randomSample(count:)做抽样验证。 -
做什么:把埋点计数、下载进度、同步状态封装进 actor。 为什么值得做:actor 适合多个任务同时访问的可变状态,演讲中的
Statistics就是计数器示例。 怎么开始:把原来的class Statistics改成actor Statistics,把可变属性设为private,从外部调用时加await。 -
做什么:为内部工具或 Swift 包生成可发布文档。 为什么值得做:DocC 集成在 Xcode 13,使用 Swift 源码里的 Markdown 注释生成文档。 怎么开始:先给公开类型补 Markdown 注释,再用 Xcode 的 DocC 工作流生成文档归档。
关联 Session
- Meet async/await in Swift — 从语法和控制流角度讲解
async、await、throws的组合。 - Explore structured concurrency in Swift — 展开
async let、任务组、取消和父子任务关系。 - Protect mutable state with Swift actors — 解释 actor 如何隔离可变状态并避免数据竞争。
- Swift concurrency: Update a sample app — 用一个示例 App 展示并发模型迁移路径。
- Meet the Swift Algorithms and Collections packages — 深入介绍 Collections 的数据结构和 Algorithms 的序列算法。
评论
GitHub Issues · utterances