Apple Swift 团队在 WWDC26 实验室里说了什么
Apple 把 WWDC26 的 Swift Group Lab 发布为一小时未经脚本的问答,由来自 Swift、Foundation 与服务器网络团队的四位工程师参与,然后在没有字幕的情况下推出。1 我们把这段视频送进本地的转录流程,看看他们到底说了什么。经过打磨的议程只告诉你某项功能能做什么,而实验室才是 Holly 坦承自己在一项核心并发决策上判断错误的地方、是 Corey 解释那个在已取消代码中会悄然失效的清理模式的地方,也是整个小组不断重复那句营销永远不会说的建议的地方:别再收集语言功能了。坦率的版本就在这里。
关于出处的说明:Apple 并未为这场实验室发布官方逐字稿。下文每一段引述都是根据已发布视频的本地转录所做的转述,附上大致的时间戳。发言者依逐字稿所示,以名字与所属团队标注:Holly(Swift 语言团队,泛型与并发)、Corey(Swift 服务器网络团队)、Tony(Foundation 与标准库)以及 Doug(Swift 语言团队)。1
TL;DR
- Holly 表示,若团队今天重新设计 Swift 并发,nonisolated async 函数从第一天起就会在调用方的上下文中执行。她「很长一段时间」抱持相反的信念,后来因现实世界的证据而改变想法。Swift 6.2 已将调用方上下文这一默认行为定为标准。1
- 反复出现的指引:刻意把类型设为非
Sendable。~Sendable(SE-0518,已在 Swift 6.4 中实现)提供了更干净的写法,而 Foundation 正在重新标注那些它先前标为禁止的类型,其中 UserDefaults 的全局实例将获得 Sendable 一致性。21 - Corey 的异步清理模式:已取消的上下文会悄然跳过异步清理,因此请检查每一个
async defer,找出会挂起的await调用,并用 cancellation shield 将它们包起来。SE-0493(asyncdefer)与 SE-0504(withTaskCancellationShield)都会在 Swift 6.4 登场。34 - 对技术路线图的坦白:
Disconnected类型仍是论坛上的活跃提案,tuple 一致性「快完成了」但形式上又退回到修订阶段,UniqueArray原则上已获接受,Subprocess 1.0 即将发布,而 SwiftPM 正统一改用 Swift Build。5678 - 小组反复出现的主题:「语言功能不是收藏品。」先做剖析,采用 profiler 指向的那个窄用途工具,其余的就别去碰。1
Apple 今天会做出不同决定的并发抉择
Holly 的并发回顾大约从 33:42 开始。整场实验室没有官方字幕;以下逐字稿来自本地 ASR。
一位开发者向小组提出了一个经过打磨的议程很少邀请的问题:既然 Swift 6 并发已在实际环境中运行得够久、足以观察采用情况,团队今天有没有什么会想用不同方式去设计?Holly 毫不含糊地回答:当然有。
具体的遗憾在于 nonisolated async 函数究竟在哪里执行。两项 Swift Evolution 提案把这个行为推往相反的方向。第一项让 nonisolated async 函数一律跳转到全局并发线程池;后来那项在 Swift 6.2 中,让它们留在调用它们的上下文上,因此从 main actor 调用的函数就会留在 main actor。1 Holly 把这次反转归因于那些在真实 App 与库中运行早期并发检查标志的人所提供的证据:他们在 actor 隔离上下文与这些 nonisolated async 函数之间来回传递非 Sendable 类型,而全局池这一默认行为产生了数据竞争错误,因为原本的 actor 在 async 函数于他处运行时仍持有那些值。1
接着是任何议程录像中都不会出现的那一段。「我很长一段时间都抱持这个信念,认为在全局并发线程池上运行才是着眼长期效益的正确模型,」Holly 说,「但随着时间推移,现实世界项目中的问题说服了我」(ASR 转述,约 35:50)。1 Doug 把最初的决定诠释为对整体系统合理的押注:可用的并发越多,潜在的并行性就越多,这可能意味着更好的性能。代价只有在人们动用完整的安全模型后才浮现,而它把太多类型往 Sendable 推,Doug 称这「并非表达所有这些概念的自然方式」。1 调用方上下文这一默认行为更平易近人,因为在你明确选择并行性之前,它的行为就跟非并发代码一样。如果你采用了 Swift 6.2,并好奇为什么并发的整体感受突然变得平静许多,答案就是:设计它的人承认最初的默认是错的,并推出了修正。
刻意把你的类型设为非 Sendable
实验室里最反直觉的建议,与两年来的迁移习惯背道而驰。Holly 说她不断遇到开发者问如何让自己的类型变成 Sendable,而更好的问题往往是反过来的:这个类型是否应该是非 Sendable?1 对于在单次计算内使用的短暂类型,明确标记为非 Sendable 会产生更干净的数据模型,也在「你打算跨隔离边界传递的东西」与「你不打算传递的东西」之间划出更清晰的界线。这也有助于性能,因为传递类型的成本,正是你想在那些根本不需要移动的中间值上避免支付的。1
Swift 6.4 为这个意图提供了真正的写法。~Sendable(SE-0518)让你直接抑制这项一致性,就像 ~Copyable 抑制可复制性一样,而不必编写一个 unavailable 一致性。2 Holly 把这个区别讲得很精准:unavailable 的 Sendable 一致性声明该类型及其每一个子类都绝对不可发送,并把这点向整个层级往下传播;而 ~Sendable 只是缺少一致性而已,因此一个没有新增可变状态的子类,仍然可以加上自己的 Sendable 一致性。21
这份灵活性正是 Foundation 所需要的。Tony 解释了 Foundation 最初 Sendable 审计中的第三类:父类安全、但子类可能不安全的类。他举的例子是 NSString,它不可变且可发送,相对于 NSMutableString,后者是可变的子类且不可发送。1 Foundation 没有仓促套上错误的标注,而是把这些模棱两可的类标为非 Sendable 并静观其变。Tony 在实验室里说,现在团队正使用 Swift 6.4 的标注来准确地重新标注这些类型,而他预期全局的标准 UserDefaults 将获得 Sendable 一致性。1 请把 UserDefaults 的一致性与更广泛的 Foundation 重新标注视为实验室出处的说法:Tony 在问答中陈述了这两点,而它们并非我能从已发布提案中独立确认的功能。小组长久以来的警告在此同样适用,这次出自 Tony:别为了强行进入 Swift 6 模式而动用 @unchecked Sendable,因为每一个 unchecked 标注都会舍弃编译器原本能为你证明的安全性。1
异步清理,做到它真的会执行
Corey 负责实验室里最实用的一项修正,而且这正是那种你会在不知不觉中发布出去的 bug。结构化并发的招牌优势在于自动传播取消:取消一个 task,所有为它服务而衍生出来的东西也会被取消,这正是 view 被关闭或客户端断开连接时你想要的结果。1 陷阱藏在清理里。你可能有一个写到一半的文件需要刷新到已知良好的状态,或是一笔需要回滚的数据库事务,而这个清理本身就是异步的。如 Corey 所说,大多数 Swift 代码在已取消的上下文中拒绝执行,因为它假定自己应该尽快收尾,因此在已取消 task 内运行的异步清理,可能会悄然无法完成它该做的事。1
解法是 cancellation shield,而它与 async defer 直接搭配。SE-0493 让 defer 块得以包含 await 调用,将在 Swift 6.4 登场。3 SE-0504 新增 withTaskCancellationShield,同样在 Swift 6.4,它会像未曾请求取消那样执行一个块,好让会挂起的清理得以完成。4 Corey 的审计诀窍很具体:检查每一个 async defer 块,自问里头的任何 await 是否真的会挂起。如果会,就把它包进 cancellation shield。1 他称这两者是「绝佳的搭档」,也是「把资源漂亮收拾干净的一记真正出色的组合拳」。1 如 Corey 所言,非 Sendable 类型强化了同样的纪律,因为一个无法跨越并发域的值,会迫使你的异步控制流保持线性、易于推理。1
Corey 对结构化并发更广泛的建议为本节作结。要果断地拥抱它,因为麻烦正来自那些逃生口。除非你是刻意把工作送往他处,否则「几乎要不计一切代价」避开非结构化 task(Task.detached 或裸 Task),而且在主线流程中绝对不要用。把 task group 当作你的默认选择,让对象的生命周期贴合其词法作用域,并编写线性的异步代码:一份先 A、再 B、再 C 的步骤清单,只有在工作真正扩散时才在 task group 内部动用并行性。1
对技术路线图的坦白
实验室也是小组把已发布功能与乐观预期区分开来的地方,而当你在决定要在什么之上去构建时,这道落差很重要。
如何存储一个已转移的非 Sendable 值,这个问题有个真正的答案正在路上。今天你用 sending 关键字进行转移,而以区域为基础的隔离让编译器得以证明原本的所有者已释放访问权;但如果你需要存储这样的值,就只有那些不安全的 opt-out 才行得通。1 Holly 指向 Disconnected,这是论坛上一个活跃的提案,提议一种能在存储过程中保留转移特性的类型,让你可以先把一个值收起来、稍后再交出去。它正在设计与讨论之中,尚未实现。51
Tuple 一致性是工程师乐观与形式状态之间最鲜明的例子。当被问到 tuple 要有条件地一致于 Equatable、Hashable 与 Comparable 还缺什么时,Holly 解释这是 parameter pack 的延伸:语言需要一套语法,用来为元素类型是 parameter pack 的 tuple 编写 extension,并以 where 子句要求每个元素都一致。1 她说有一个实验性实现已存在于编译器的代码库中,而且「几乎完全到位了」。1 形式上的记录则更为谨慎。SE-0283,也就是最初的 tuple 一致性提案,已被退回修订,其 2020 年的实现也被还原;通用化的 parameter pack 做法如今只存在于一个实验性的 TupleConformances 编译器标志之后。10 请把 Holly 的「快完成了」读作一位工程师对实现的诚实判断,而非一项已发布的功能。
性能容器的进展更快。Tony 提到的 UniqueArray 出自 SE-0527(「RigidArray 与 UniqueArray」),现已原则上获得接受,引入一个全新的标准库 Containers 模块;它已以早期形式随 swift-collections 1.3 发布。7 Tony 把它定位为某种 Array 的升级选项——也就是那种写时复制的 retain/release 流量会在 Instruments 追踪中现形的 Array。1 Subprocess,这个 Tony 点名为自己最爱的跨平台开源 process API,今年将发布 1.0 版;1.0 里程碑已宣布,而最新发布的标签仍停在 0.5,因此它是即将发布而非已发布。61 在工具方面,Holly 描述了开发者也许不会注意到的 SwiftPM 变动:Xcode 的包构建与开源工具链的构建现在共用同一套实现——Swift Build,它正逐步成为默认的构建系统后端(在 main 上已是默认,目标为 6.4)。81
最后,是 Holly 一开始就提到的迁移路径:@diagnose,出自 SE-0522(「Source-Level Control Over Compiler Warnings」)的新属性,已获接受。9 它提供源代码级别的警告控制,让你能在某个区域抑制 deprecation 警告,或在最要紧的范围内选择启用默认关闭的警告,例如 strict memory safety 或 strict concurrency,这使它成为分阶段 Swift 6 迁移更细致的工具。1
工程师指引,集结成册
实验室的价值在于那些累积起来、永远塞不进一场议程时段的实务建议。重点如下,附上出处:
- 编写线性的异步代码。 Corey:几乎不计一切代价避开非结构化 task,让每个 task 都是一份顺序的步骤清单,只在工作真正扩散时才于 task group 内部引入并行性。1
- 一份 MainActor 迁移诀窍。 Holly:从叶节点类型开始,由内往外推进;或者对一个应该完全属于 main actor 的模块开启 main-actor-by-default,再把那些卸载出去的部分明确标注。把不触碰任何可变状态的方法标为
nonisolated,并把一个从未真正被改动的static var转成let,好让它得以保持 nonisolated。1 - 一致性的成本并不对等。 Doug:一个未使用的
Equatable或Hashable一致性会把它的 witness 代码留在二进制文件中,因为运行期的as?对 existential 的转型有可能发现它,所以它会耗费代码大小;Sendable则是纯粹的编译期标记,没有运行期表示,因此一个未使用的Sendable一致性是免费的。1 @inlinable加@inline(never)的组合。 Corey 最爱的技巧:@inlinable把函数主体跨越模块边界暴露出来,解锁泛型特化与 effects 传播;而@inline(never)则让一条冷僻、代码庞大的路径(按字节的后备复制)不被内联,好让热路径保持快速。他在「整个 Swift 网络代码组合里用过这对组合三次」,用于不寻常的冷路径性能问题。1- 优化最热的路径,而非整个项目。 Corey:「我们仅仅沿着最热的路径引入 span 和几个 unique array,就获得了极大的成功」,以一项小而有编译器支持的变动,交付了几乎全部的性能收益。让这些新的性能类型保持隔离,这样采用它们就永远不会逼你做一次庞大的重构。1
- 别再收集功能了。 小组反复出现的那句话,出自 Corey 引述的一位朋友:「语言功能不是收藏品。」用上全部功能并不会给你奖赏。先做剖析,伸手去拿 profiler 指向的那个窄用途工具(non-copyable 类型、
UniqueArray、Span),把其余的时间花在修 bug 与做功能上。1
常见问题
Apple 真的把 WWDC26 的 Swift 实验室在没有字幕的情况下发布了吗?
是的。Apple 在开发者网站上把 Swift Group Lab(议程 8001)以一段没有官方逐字稿或字幕的视频形式发布。1 为了准确引述,我把录像送进本地的转录流程,因此本文中每一段引述都是出自那份本地 ASR 的转述,附上大致的时间戳,并以名字与团队标注。1
Swift 团队说他们会把并发改成什么样子?
Holly 说,若 Apple 今天设计 Swift 并发,nonisolated async 函数从一开始就会在调用方的上下文中执行,而非跳转到全局并发线程池。她长期抱持全局池的信念,后来因现实世界的证据而改变想法,Swift 6.2 已把调用方上下文的行为定为默认。1
我该让 Swift 类型变成 Sendable 还是非 Sendable?
实验室的建议是刻意把短暂的计算类型设为非 Sendable。Swift 6.4 为此新增了 ~Sendable 写法(SE-0518),它有别于 unavailable 一致性,因为当子类未新增可变状态时,仍可加上 Sendable。2 Tony 说 Foundation 正用这项新功能重新标注其模棱两可的类,并预期全局的 UserDefaults 将获得 Sendable 一致性,两者皆为实验室出处的说法。1
当 task 被取消时,我该如何确保异步清理会执行?
已取消的上下文会使大多数 Swift 代码跳过会挂起的工作,因此异步清理可能悄然失效。Corey 的模式是检查每一个 async defer,找出真的会挂起的 await 调用,并把它们包进 cancellation shield。Async defer(SE-0493)与 withTaskCancellationShield(SE-0504)都会在 Swift 6.4 登场。341
Tuple 一致性会在 Swift 中发布吗?
还没有。SE-0283 已被退回修订,其原始实现也被还原;通用化的 parameter pack 做法只存在于一个实验性的 TupleConformances 标志之后。Holly 在实验室里说,编译器中的一个实验性实现「几乎完全到位了」,这反映的是工程师的乐观,而非一项已发布的功能。1
实验室是这些公告的坦率伙伴:想了解已发布功能在上下文中的样貌,请阅读 Swift 2026 有什么新东西;想了解 Holly 所谈调用方上下文默认背后的迁移机制,请见 Swift 6.2 并发实战。小组「先剖析、再采用窄用途工具」的哲学也延伸到了测试,详见 Swift Testing 对决 XCTest。完整的 WWDC26 报道收录于 Apple Ecosystem Series。
参考资料
-
Apple, WWDC 2026 session 8001, Swift Group Lab. Apple published no official transcript or captions for this lab; all quotes attributed to it are paraphrases from a local transcription of the published video, with approximate timestamps. Source for the concurrency retrospective (Holly, ~33:42), the make-types-non-Sendable guidance, the Foundation re-annotation and UserDefaults Sendable conformance (both lab-attributed to Tony), the async-cleanup and cancellation-shield pattern (Corey), the structured-concurrency and MainActor migration advice (Corey and Holly), the conformance-cost explanation (Doug), the
@inlinableplus@inline(never)combo and hottest-path performance guidance (Corey), theDisconnected, tuple-conformance,UniqueArray, Subprocess, SwiftPM, and@diagnoseremarks, and the “language features aren’t collectibles” theme. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple / Swift Evolution, SE-0518,
~Sendablefor explicitly marking non-Sendabletypes. Status: Implemented (Swift 6.4). Source for suppressing theSendableconformance and the distinction from an unavailable conformance. ↩↩↩↩ -
Apple / Swift Evolution, SE-0493, Support
asynccalls indeferbodies. Status: Implemented (Swift 6.4). Source forawaitinsidedeferblocks. ↩↩↩ -
Apple / Swift Evolution, SE-0504, Task cancellation shields. Status: Implemented (Swift 6.4). Source for
withTaskCancellationShield. ↩↩↩ -
Swift Forums, Pitch:
Disconnectedtype for modeling disconnected values. Active forums pitch for safely storing transferred non-Sendablevalues; not implemented. ↩↩ -
Apple / Swift, Subprocess — cross-platform, open-source process API announced in 2025; version 1.0 announced as releasing this year, with the latest published tag at 0.5. Status per the lab and the project’s published releases. ↩↩
-
Apple / Swift Evolution, SE-0527, RigidArray and UniqueArray. Status: Accepted in Principle; introduces a new standard-library Containers module and ships in an early form in swift-collections 1.3. ↩↩
-
Swift Forums, SwiftPM development update: default build system change. Swift Build is becoming the default SwiftPM build-system backend (default on main, targeting 6.4). ↩↩
-
Apple / Swift Evolution, SE-0522, Source-Level Control Over Compiler Warnings. Status: Accepted. Source for the
@diagnoseattribute and region-scoped warning control. ↩ -
Apple / Swift Evolution, SE-0283, Tuples Conform to
Equatable,Comparable, andHashable. Status: Returned for revision; original implementation reverted. Source for the formal status of tuple conformances against the experimentalTupleConformancesflag. ↩
