大多数浏览器和
Developer App 均支持流媒体播放。
-
Swift 的新功能
与我们一起了解 Swift 的最新更新。我们将讨论有助于提高效率的工作流程改进,以及用于处理基本编程任务的全新现代 API 资源库。我们还将举例说明 Swift 在软件堆栈各层的广泛采用情况。最后,我们将探索新的语言特性,这些特性有助于提高并发易用性,同时确保在需要时实现最佳性能。
章节
- 0:00 - Introduction & Agenda
- 0:48 - swiftlang updates
- 3:06 - Development workflow: Writing code
- 4:40 - Development workflow: Building
- 7:36 - Development workflow: Debugging
- 9:14 - Libraries: Subprocess
- 10:45 - Libraries: Foundation
- 12:31 - Libraries: Observation
- 14:13 - Libraries: Testing
- 16:08 - Swift throughout the stack: Embedded Swift
- 18:00 - Swift throughout the stack: Security
- 19:37 - Swift throughout the stack: Server
- 23:23 - Swift throughout the stack: Platforms
- 26:11 - Language evolution: Performance
- 30:28 - Language evolution: Concurrency
- 37:15 - Wrap up
资源
相关视频
WWDC25
- 了解 Containerization
- 优化 Swift 代码的内存使用和性能
- 安全地混合使用 C、C ++ 和 Swift
- 探索 Swift 和 Java 互操作性
- 跟着视频学编程:使用 Swift 并发机制提升 App 性能
- 采用 Swift 并发
WWDC24
-
搜索此视频…
大家好 欢迎观看 “Swift 的新功能”讲座 我是 Holly 我是 Allan 今天我们将介绍 Swift 6.2 中的新功能和改进 这些改进旨在帮助你成为一名更高效 的 Swift 程序员 无论你选择在哪里 编写代码 也无论你编写哪种代码 都能助你提高效率 首先 我们将介绍编写、构建和 调试代码时 所遵循的工作流程有哪些改进 然后 我们将介绍用于处理 基本编程任务的全新 Library API 此外 我还将介绍如何在软件堆栈的 每一层采用 Swift 最后 我们将探索新的语言功能 这些功能使并发机制更易于实现 并有助于你在需要时 实现峰值性能 我们今天介绍的所有更改 都是在开源中共同开发的 GitHub 上的 swiftlang 组织已经 发展壮大到拥有 50 多个项目 包括编译器、swift.org 网站、 基础库和测试库 以及其他支撑更广泛生态系统 的关键组成部分 除了将项目迁移至 swiftlang 我们还开源了 Xcode 的构建系统 即 Swift Build Swift Build 还支持 Apple 操作系统 的构建流程 开发者正在通过开源方式采用 Swift Build 作为 Swift Package Manager 的底层构建系统 这将统一 Xcode 与 swift.org 工具链之间的构建引擎 要进一步了解 Swift Build 请查看 “The Next Chapter in Swift Build Technologies”博客文章 Swiftlang 项目的一个最新成员 是名为 Swiftly 的版本管理器 Swiftly 最初是由开源社区开发的 用于简化 Linux 上的 Swift 工具链管理 Swiftly 现在支持 macOS 并且 swift.org 上已提供 1.0 版 只需输入一个命令 便可以 安装最新的 Swift 工具链 或者 你也可以从 GitHub 分支 安装一个夜间快照 以试用开发中的语言功能 在 Xcode 中 可通过 Toolchain 的菜单访问 Swiftly 安装的工具链 请记住 如果你正在构建 App 你仍需要使用 Xcode 工具链 将你的 App 提交到 App Store 在 VS Code 中 你可以使用 Toolchain 命令来 选择 Swiftly 安装的工具链 要进一步了解 Swiftly 1.0 你可以查看 Swift 博客 Swift 入门体验的更新 不仅仅限于安装工具 Swift.org 主页焕然一新 更专注于帮助你深入探索不同的领域 从 App 开发到云服务 再到嵌入式系统 这项工作是与 Swift Website 工作组合力完成的 上手使用 Swift 从未如此简单 Allan 将介绍 Swift 6.2 如何让你更灵活地 打造一个合适你的开发环境 谢谢 Holly 今年的 Swift 版本 针对开发工作流程 进行了诸多改进 无论你选择如何编写 Swift 代码 都能充分获益 这些改进涵盖各种工作流程 包括编写代码、构建以及调试 我们先来看看今年有哪些适用于 VS Code 开发者的新功能
VS Code 市场中的 Swift 扩展 现已由 swift.org 验证和分发 它在过去的一年里新增了许多新功能 第一项功能是后台索引 在 Swift 6.1 中 我们默认针对 VS Code 和其他 IDE 中的 Swift PM 项目启用了后台索引 这样一来 在你更改项目时 诸如跳转到定义等编辑器功能 便会保持最新状态 得益于 SourceKit-LSP 所作的改进 VS Code 中的代码补全结果 现在变得更相关 此外 调试也得到了简化 现在 当你安装 Swift 扩展时 LLDB 支持会自动添加 这个扩展的最新版本 还包含一个新的项目面板 让你可以轻松找到软件包的 依赖项、目标和任务 最后 VS Code Swift 扩展 还提供了一种新的方式 供你查看项目的文档 你可以调出与代码并排显示的 DocC 预览功能 这个功能会在你键入代码时实时更新 VS Code Swift 扩展是 在开源中开发的 你可以在 swiftlang 组织中 的 GitHub 上找到它 接下来 我们将谈谈 Swift 代码 构建方面的一些改进 无论在哪里 这些改进都适用于你 对工作效率影响最大的一个方面 是工具的性能
对于那些使用基于宏的 API 的项目 Swift 6.2 显著缩短了 清洁构建的时间 为了一探究竟 假设你有一个项目 它使用名为 Stringify 的 虚拟软件包中的宏 以前 为了构建你的项目 Swift PM 必须先获取 swift-syntax 的源代码 Swift-syntax 是一个 用于为宏提供支持的库 然后,它将构建 swift-syntax、 构建 Stringify 宏插件 并最终构建你的代码 尽管 swift-syntax 的构建结果 可能被缓存起来 但它仍会导致执行干净构建 所需的时间延长 这一点在持续集成环境中 可能尤为明显 为了加快构建速度 最新版本的 Swift PM 和 Xcode 支持预构建的 swift-syntax 依赖项 这完全消除了开销较大的构建步骤 对于某些项目 这还会将干净构建的时间缩短数分钟
如果你拥有一个提供宏的软件包 你的客户端将能够 充分利用这项优化功能 只要这个软件包依赖于 swift-syntax 的标记版本即可 你的工作效率并不总是 只与构建性能有关 有时 尝试解决编译器发现的问题 会放慢你的速度 例如 当你在 Swift 6 语言模式下 编写并发代码时 编译器可能会检测到 你需要防范的潜在数据争用 这条错误信息指出了一个 比较重要的问题 但并没有明确指明如何解决这个问题 因此 我们在 Swift 6.2 中 开始对文档内容进行扩充 针对很多类型的诊断 提供了相关的文档说明 这些针对常见警告和错误的解释说明 可帮助你了解问题 并为你提供了具体的解决方案 你可以从 IDE 中或在 swift.org 网站上访问相关文档 控制诊断程序的工作方式也很重要 例如 要确保代码不显示警告 你可以使用 “warnings as errors”设置 这项设置适用于那些 比较容易处理的警告 但对于某些警告 比如那些涉及已弃用的 API 的警告 你可能不希望被强制要求立即处理 SWIFT 6.2 让你能够灵活地决定 将哪些警告视为错误 例如 你可以决定默认将 所有警告均视为错误 但已弃用的声明则除外 另一方面 如果你只想阻止显示 某些类别的警告 你可以直接将相应警告升级为错误 针对编译器警告添加控制功能 充分彰显了整个社区为提升 所有开发者的 Swift 开发体验 而共同付出的努力 感谢提案撰写者 以及在论坛上 参与讨论的每一个人 最后 我们来看看 Swift 6.2 中 在调试方面做出的一些重大改进
在这个版本中 我们大幅提升了 异步代码的调试体验 在你逐步调试某个 Swift 任务 中运行的代码时 LLDB 现在会跟随执行流程 进入异步函数 即使需要在线程之间切换也不例外 调试器对 Swift 以任务为中心 的模型的理解 使它能够显示哪个任务正在执行 你甚至可以命名一个任务 让它更便于识别 任务名称还会显示在 Instruments 描述文件中 这些文件记录在 Instruments 并发模板中 你还可以使用 swift task info 等新命令 获取当前所执行任务的更多信息 例如任务的优先级及其子任务 最后 你的调试会话从今年起 将变得更灵敏 这要归功于显式构建的模块 这一构建系统功能 这项功能可在构建模块依赖项时 实现并行性和重用性 在显式构建的模块推出之前 Xcode 构建和调试器会使用 完全独立的模块图 现在 调试器能够重用 构建时生成的模块 这意味着首次在调试器中 运行 P 或 PO 时 这个命令的评估速度将明显变快 因为类型信息会立即可用 Xcode 26 中默认会启用 显式构建的模块 今年我们在工具方面 对 Swift 进行了众多改进 刚刚只介绍了其中的一小部分 接下来 我将带你了解一些我们 针对日常使用的 Swift 核心库 所做的重要更新 库可帮助你完成各种各样的任务 Swift 的核心库 提供了一些重要的构建块 用于编写 Swift 代码 Swift 6.2 针对 Foundation 的更多 API 表面进行了现代化改进 并提供了一些新 API 来 简化日常编程任务 为了提升使用 Swift 完成 脚本编写任务的体验 Foundation Workgroup 推出了 一个新的软件包 其中包含用于启动子进程的 API 首先应添加并导入 新的 Subprocess 软件包 然后 通过调用 run 函数并等待结果 来启动一个子进程 如果你提供一个 带有进程名称的字符串 run 方法将基于 $PATH 环境变量来 查找可执行文件
大多数情况下 你需要使用 FilePath 指定某个可执行文件的路径 以启动一个子进程 当子进程终止时 你可以检查退出状态、 标准输出以及其他 有关进程执行情况的信息 子进程库提供了更多功能 让你能够 对进程执行、特定于平台的配置选项 等内容进行精细控制 你可以在 swift-subprocess 存储库 中探索完整的 API 接口集合 这个软件包的当前版本为 0.1 版 你在采用期间提供的反馈将 为我们在 1.0 版中发布的 API 提供参考 Foundation Workgroup 还对 App 项目中最常用的 日常 API 进行了改进 在 iOS App 中 针对来自 UIKit 的 通知做出响应是一项很常见的操作 但用于观察通知的代码很容易出错 首先 你必须小心地注册一个 对象支持发布的通知名称 如果出现错误 你的通知回调将永远不会运行 与通知有关的信息储存在 非类型化的字典中 这要求你使用正确的键 手动进行下标访问 并将所得结果动态转换为正确的类型 即使能保证通知 在主线程上发布 你在访问主 Actor API 时 仍会收到并发错误 通知名称和有效负载 现在支持具体类型 具体类型让编译器能够检查 对象是不是支持你所注册的通知类型 它们还会在处理通知有效负载时 消除样板代码 通知类型指定了通知的发布位置 如果通知发布在主 Actor 上 你可以自由访问 主 Actor API 遵从 MainActorMessage 可确保通知 始终在主线程上同步发布 遵从 AsyncMessage 意味着通知 在任意线程上异步发布 对于由 UIKit 和 Foundation 等框架发布的通知 SDK 中提供了具体的通知类型 你也可以为自己的通知 添加具体的通知类型 向注册的观察器广播系统通知 基于更通用的观察器模式 Observation Library 提供了通用 API 用于自动追踪对象图中的状态变化
你可以使用 @Observable 宏 将一个类纳入 观察追踪的范围 Swift 6.2 推出了一种通过 AsyncSequence 从可观察类型中 流式传输状态变化的方法 首先使用闭包来创建 新 observations 类型的实例 在闭包中 你需要计算 要观察其变化的值 在这里 我想观察一个 描述玩家当前状态的字符串 并希望每次更新分数或玩家获得 新项目时 都能获得一个新的值 你将获得一个基于闭包中 使用的可观察属性 而更新的值 更新以交易方式进行。 当闭包中任一可观察属性的 willSet 被调用时 将开始追踪更新 追踪进程将在代码暂停的 下一个 await 处结束 更新后的值将包含 针对这两个代码点之间的 其他属性所做的所有同步更改 这可以确保针对多个属性进行的 同步更新不会 在对象处于不一致状态时 引发观察更新
如果我同步更新分数和项目 我只会获得一个更新后的值 其中包含这两项更改 观察结果类型遵从 AsyncSequence 因此 你可以使用 for-await 循环 来迭代更新后的值 作为开发者 我们每天都会编写测试 Swift Testing 是一个 跨平台库 提供了多个 用于描述和组织测试的宏 比如 用于声明测试函数的 @Test 属性 你使用 expect 和 require 宏 来验证自己的假设 当预期或要求失败时 Swift Testing 会为你提供 可操作的信息 但有时 测试失败很难进行分类 尤其是当测试仅在 CI 等 远程环境中失败时 了解测试失败可能需要获取更多 有关测试所用数据的上下文
Swift 6.2 推出了自定附加功能 来帮助你诊断测试失败 你可以调用 Attachment.record 方法将附加功能添加到测试 你可以附加 Data 和 String 等 库类型 并可以通过遵从 attachable 协议 来针对你自己的类型实现附加支持 Swift 6.2 还支持退出测试 用于测试你期望在某些条件下 终止的测试代码 例如 如果你编写一个函数以 通过 precondition 来验证 有关输入参数的假设 你可以编写一个 用于测试 precondition 是否失败的测试用例
你通过将 processExitsWith 参数 传递给 #expect 或 #require 来编写退出测试 当你编写退出测试时 Swift Testing 将启动一个新的 进程来运行这个测试 你可以通过某一特定的退出代码或 信号或通过任何失败状态 来验证进程是否成功退出 这些通用库让你可以在 整个项目中 甚至在应用程序代码的外部 编写和测试可移植 Swift 代码 在 Apple 我们在整个软件堆栈中 使用 Swift 我们将它用于固件、App 和框架 以及大规模的网络服务 在针对嵌入式设备、 关乎安全的组件以及服务器 编写代码时 你可以充分利用 wift 6.2 中的改进 你还可以针对一些新平台 编写代码 我们将从堆栈底部的 Embedded Swift 开始介绍 Embedded Swift 是 Swift 的子集 用于针对最受限的环境编写代码 这类环境包括嵌入式设备的固件 以及操作系统内核等 它是一个支持 Swift 核心特性 的编译模式 这类特性包括值和引用类型、 闭包、可选类型 错误处理、泛型等 在 Apple 我们将 Embedded Swift 应用于在 iPhone 上 运行的一些最底层软件中 例如在 iOS 26 中 Embedded Swift 运行在用于管理 CPU 与 GPU 之间的 共享内存页面访问权限的协处理器上 如果你想试用 Embedded Swift 你可以观看 “利用 Embedded Swift 实现 轻量级开发”讲座来了解更多信息 Swift 6.2 推出了一些新功能 实现了对 Embedded Swift 的扩展 它现在涵盖 Swift 的全部 字符串 API 其中包括字符串插值 现在 当某一协议受限于 class 类型 时 可以使用 Swift 的 any 类型 这个类型可以表示那些 遵从这个协议的类型的值 Swift 标准库还引入了 InlineArray 和 Span 这是两个用于高效处理 内存区域的新 API 这些类型适用于那些 常见于嵌入式程序 并且对性能要求很高的代码 稍后 Holly 将更详细地 介绍这些类型 随着嵌入式 Swift 变得越来越强大 社区一直在忙于创建示例 你可以参考这些示例来 开始创建自己的嵌入式项目 要查看这些示例 请访问 GitHub 的 swift-embedded-examples 存储库 Embedded Swift 的好处之一是 让你可以用一种默认情况下内存安全 的语言来编写底层软件 内存安全代码的安全性更高 我们一直在努力改进 Swift 使其更适合编写在安全性方面 要求极为苛刻的代码 有时 你需要编写代码 来完成从根本上而言并不安全的事情 例如 在与用 C 语言编写的依赖项 整合时 通常使用一个 采用指针的 API 在安全性至关重要的上下文中 应尽可能避免不安全的代码 但如果无法避免 则应该让不安全代码易于识别 这就是我们为什么在 Swift 6.2 中 推出了一项名为严格内存安全的 全新可选功能 这个模式要求使用的所有不安全 API 都能在源代码中得到明确的识别 这个模式所需的注释可帮助你识别 代码的哪些部分需要从安全性角度 受到额外的关注 除了这个新模式外 Swift 6.2 还 支持为 C 和 C++ 标题添加新的注释 这些注释会导致 API 能够 以诸如 Span 这样 安全且符合人体工程学的类型 导入 Swift 要进一步了解新的互操作性功能 请观看“安全地混合使用 C、 C ++ 和 Swift”讲座 在 Apple 的操作系统中 我们将在 两个安全性至关重要的组件中 采用严格内存安全功能:分别是 WebKit 和“信息”App 的子系统 这个系统会分析收到的信息和附件 由于这两个组件都会处理不受信任的 输入 因此应限制不安全 API 的使用 这一点至关重要 接下来 我们了解一下 Swift 在服务器生态系统中的应用
Swift 在我们的 Apple 服务中 发挥着至关重要的作用 后台服务每秒钟会处理数百万个请求 这些服务均由 Swift 提供强大支持 其中一项服务负责 提醒用户留意被盗的密码 因为它们在数据泄露事件中被发现 这项服务以前是用 Java 构建的 最近用 Swift 进行了重写 这样做的好处是显而易见的 得益于 Swift 高效的原生代码和 确定性内存管理机制 服务的吞吐量提升了 40% 而硬件要求则降低了一半
在服务器上采用 Swift 会 给你们中的很多人带来好处 例如 Cultured Code 的工程师 用 Swift 针对广受欢迎的 “Things”App 重新实现了后台服务 Things Cloud 并且在 swift.org 上 分享了相关体验
对于 Things Cloud 使用 Swift 会使计算成本降低三倍 同时会使平均响应时间缩短 400% 除了提升性能外 采用 Swift 的另一个好处是 能够使用相同的语言和工具进行 客户端和服务器开发 团队之所以能够使用 Swift 构建服务器后端 一个关键因素在于 Swift 丰富的库 Swift 拥有一个不断扩大的 软件包生态系统 涵盖各种各样的重要使用场景 Swift 提供了适用于联网、 数据库驱动程序、 可观测性、消息流等内容的资料库 今年一个值得关注的更新是 gRPC Swift 版本 2 的发布 gRPC 是一个用于构建服务 API 的现代化、高性能软件包 版本 2 的亮点之一是现代化的 API 它们利用 Swift 并发机制、 以惯用方式生成的 Swift 代码 以及可插拔的高性能传输层 gRPC Swift 的维护者在 swift.org 上写了一篇关于更新的博客文章 请查看这篇文章来了解更多信息 与 C、Objective-C 和 C++ 的互操作性是 Swift 核心优势之一 通过一次更新一个文件的方式 采用 Swift 你可以逐步改进用这些语言编写的 现有代码库 在服务器生态系统中 许多现有 应用程序都是用 Java 编写的 我们认为在这些代码库中 也非常适合以增量方式采用 Swift 这就是为什么我们在去年宣布推出了 一个激动人心的新开源项目: swift-java 它的目的是使各种语言 能够实现无缝整合
通过 swift-java 你可以创建绑定来 允许 Swift 代码 调用 Java 代码 反之亦然 这些绑定旨在包装每种语言的值 的原生表示 而不会产生大量开销 Swift 代码的 Java 绑定可以利用 Java 的新一代外部接口技术 或者在必要时回退到 JNI swift-java 项目目前 正处于实验阶段并且发展迅速 要进一步了解这个项目的工作方式 请观看“探索 Swift 和 Java 互操作性”讲座
在创建同时包含客户端和服务器组件 的 App 时 你需要设计一个 能够在本地测试你的代码在 这两个环境中运行情况的工作流程 Apple 发布了一个新的 开源 containerization 库 你可以使用这个库 基于 Mac 上运行的 Linux 容器 来构建工具 这个库完全用 Swift 实现 并且在设计时着重考虑了安全性、 隐私和性能 这个库充分体现了使用 Swift 进行系统级开发的好处 要进一步了解这个库 请观看 “了解 Containerization”讲座 你也可以访问 GitHub 上的 containerization 存储库 在那里 你可以找到用于运行容器的 命令行二进制文件 最后 我们介绍一些有关 Swift 支持的平台的更新 Swift 6.2 新增了对 FreeBSD 这一操作系统的官方支持 这个系统在服务器和嵌入式平台上 备受欢迎 此外,Swift 现在还支持 WebAssembly (也称为 Wasm) WebAssembly 是一个虚拟机平台 专注于可移植性、安全性 和高性能 开发者可以为 Wasm 构建 客户端和服务器应用程序 然后将它们部署到 浏览器或其他运行时 WebAssembly 支持最初是 一个社区项目 今年早些时候 这个项目达到了 一个重要里程碑 并发布了一份愿景文档来 阐述 Swift 中为 WebAssembly 提供的官方支持 下面我们看一看 Wasm 支持 是如何发挥作用的 我有一个用 Swift 编写的 App 它从文件中载入 3D 模型 然后在浏览器中进行渲染 接下来显示的是这个 App 目前的外观
这个 App 运行在 Wasm 虚拟机上 Swift 标志是使用 WebGPU 渲染的 这个 Web 标准目前处于实验状态
这个 App 使用 JavascriptKit 它是一个由 SwiftWasm 组织开发的 开源软件包 我可以使用这个库通过原生 Swift API 与 Javascript 运行时 进行交互 为了让最终的二进制文件尽可能紧凑 我将使用 Embedded Swift 来 编译这个 App 但总的来说 这个代码只是普通的 Swift 代码
例如 我添加了一些利用泛型的 几何图形实用工具 我的 Wavefront 对象文件解析器 使用 Swift 的原生 String API
我们将添加一些动画 我将跳转到我们渲染器的 update 方法
为了给模型添加旋转效果 标志的旋转状态需要 在每一帧都更新
我们来测试一下吧 为了重新运行这个应用程序 我从项目面板中触发了一个任务 这个任务被配置为调用 Swift Build 来编译 Wasm App 然后 它将在构建输出上 运行几个最小化工具 以确保二进制文件尽可能小 最后 它将启动一个网页服务器来 托管内容 效果看起来很棒 Swift 之所以在整个软件堆栈中 是一个绝佳选择 核心在于它是一种很棒的语言 Swift 默认会优先保证 安全性和易用性 但与此同时也针对高级编程 提供了相应工具 可帮助你编写复杂代码并进行调整 以实现最佳性能 你可以根据需要了解语言功能 你可以从基础功能开始了解 做好 准备后再深入了解高级功能
Swift 6.2 改进了语言 无论是初学者还是专家 都更容易上手使用 我们为在项目中引入并发性 提供了一条更便捷的途径 我们还让这个语言在专家编写 性能关键代码时 能提供强大的支持 我们先了解一下性能方面的更新 Array 类型是一个 按序排列的元素列表 也是 Swift 项目中 最常用的一种数据类型 Array 具备的强大功能 (如动态调整大小) 使其易于使用 并且适用于很多任务 但这种灵活性是有代价的 为了支持动态调整大小 数组会储存对堆分配的缓冲区的引用 在添加更多元素并且 数组已达到最大容量时 必须动态分配新内存并将 现有元素拷贝到新内存 如果你对性能关键代码进行分析并且 这个代码在热路径中使用 Array 则你可能会注意到数组缓冲区 存在需要消除 的内存分配或引用计数开销 如果数组的大小永远不会改变 则无需承担 因堆分配而产生的开销 InlineArray 是一个 固定大小的新数组类型 它通过内联存储来存储元素 大小是类型的一部分 并且在尖括号中位于元素类型之前
InlineArray 的元素是直接储存的 而不是储存 对包含元素的缓冲区的引用 这意味着元素可以储存在堆栈上 或直接储存在其他类型中 而无需进行额外的堆分配 InlineArray 可以储存 可拷贝和不可拷贝的类型 储存可拷贝的类型时 可以拷贝 InlineArray 本身 并且元素类型会立即拷贝 要将大小编写为类型的一部分 可以使用新的泛型功能 来允许将整数作为类型参数 和元素类型一样 InlineArray 的大小 可以从数组字面值推断出 如果你编写一个包含 1、2 和 3 的 内联数组 则类型会被推断为包含 3 个整数的 InlineArray 在编译时知晓 InlineArray 的大小 还可以实现更多的优化 例如 当索引小于大小时 消除边界检查 container 类型通常需要提供 对其底层存储的直接访问 这对于在连续内存上 执行本地处理很有用 因为无需知道原始容器的布局 例如 一个函数可能需要 允许直接对 Array 或 InlineArray 的连续存储执行操作 在 Swift 6.2 推出之前 常见解决方案是 使用不安全的指针 这些指针虽然有效 但从根本上来说是不安全的 并且容易出错
新的 Span 类型是一种抽象 可针对连续内存提供快速直接的访问 而不会影响内存安全性 标准库为所有将连续存储 用于自身元素的容器类型 (包括 Array、ArraySlice、 InlineArray 等) 提供了 span 属性
Span 维护内存安全的方法是 确保在你使用 Span 时 使连续内存保持有效状态 这样的保障机制消除了 指针固有的内存安全问题 包括释放后使用 和重叠的修改 这些问题会在编译时进行检查 而不会产生运行时开销 例如 修改原始容器 会阻止随后访问 span 修改后 你将无法再次访问 span 变量
另外 span 的生命周期 无法超过原始容器 这被称为生命周期依赖 它可以防止底层存储 在仍可以通过 span 进行访问的 情况下被销毁 要了解使用 InlineArray 和 Span 控制性能的新方式 请观看“优化 Swift 代码的 内存使用和性能”讲座 要进一步了解如何理解 Swift 代码 的性能特性 请观看“探索 Swift 性能”讲座
当程序需要同时执行多项任务时 性能的另一个方面是响应速度 并发编程并非易事 因为在多项任务之间共享内容 容易出错 从而导致不可预测的行为 Swift 6 中的数据争用安全保障机制 可以在编译时防止出现这些错误 因此你可以放心编写并发代码 而不用担心会产生 难以调试的运行时错误 但在很多情况下 你编写的最自然代码 容易出现数据争用 从而导致产生必须解决的 编译器错误 对于具有可变状态的类 比如这个 PhotoProcessor 类 只要你不对它进行并发访问 它就是安全的 它有一个 async 方法 这个方法通过计算给定图像数据的 主题来提取贴纸 但如果你尝试在主 Actor 上调用 UI 代码中的 extractSticker 你就会收到一个错误 提示这个调用可能会导致数据争用 这是因为语言中存在一些机制 会将工作隐式转移到后台 即使你从不需要代码并行运行 也是如此
Swift 6.2 更改了这个设计理念 在你选择引入并发机制之前 它默认会一直保持单线程状态
Swift 6.2 中的语言更改 使编写的最自然代码默认 不会出现数据争用 这为在项目中引入并发机制 提供了一条更便捷的途径 当你因为想要并行运行代码而选择 引入并发机制时 数据争用安全保障机制 会为你提供保护 首先 我们使得在具有可变状态的 类型上调用 async 函数变得更简单 对于那些未与特定 Actor 绑定的 async 函数 语言不会立即转移它们 而是让函数在其被调用的 Actor 上继续运行 这消除了数据争用 因为传递到 async 函数的值永远不会 在 Actor 之外发送 Async 函数仍可以在 自身的实现中转移工作 但客户端不必担心它们的可变状态
接下来 我们使得在主 Actor 类型上 实现遵从性变得更简单 这里有一个名为 Exportable 的协议 我想为我的主 Actor StickerModel 类实现遵从性 由于 export 要求 不具备 Actor 隔离机制 因此语言假设它可以从非主 Actor 那里被调用 并禁止 StickerModel 在其实现中 使用主 Actor 状态
Swift 6.2 支持这些遵从性 需要知晓主 Actor 状态的 遵从性称为*隔离的*遵从性 这是安全的 因为编译器会确保 主 Actor 遵从性 只在主 Actor 上使用 我可以创建一个 ImageExporter 类型 用于将 StickerModel 添加到 any Exportable 项目的 数组中 只要它留在主 Actor 上即可 但如果我允许从任何地方 使用 ImageExporter 编译器将会禁止将 StickerModel 添加到数组 因为从主 Actor 外部对 StickerModel 调用 export 是不安全的 借助隔离的遵从性 当代码指明 要以并发方式使用遵从性时 你只需解决数据争用安全问题
全局变量和静态变量 容易出现数据争用 因为它们允许从任何位置 访问可变状态
保护全局状态的最常见方式是 使用主 Actor 一种常见做法是用主 Actor 标注整个类 以保护它的所有可变状态 尤其是在并发任务数量 并不多的项目中 通过在项目中的所有内容上标注 @MainActor 你可以对一个完全单线程的程序 进行建模 为了让你更轻松地 对单线程代码进行建模 我们推出了一个能够 默认推断主 Actor 的模式 它可以消除因 不安全的全局变量和静态变量、 针对其他主 Actor 函数 (例如 SDK 中的主 Actor 函数) 的调用等 而引发的数据争用安全错误 因为主 Actor 默认会保护所有可变状态 另外 它还减少了基本上属于 单线程的代码中的并发注释 对于那些在主 Actor 上完成大多数 工作并将并发代码封装在 特定类型或文件中的项目而言 这个模式非常适用 它是可选的 建议将它用于 App、 脚本以及其他可执行目标 将工作转移到后台对于 确保性能仍很重要 例如在执行那些占用大量 CPU 资源 的任务时确保 App 响应灵敏 我们来看看 PhotoProcessor 上 extractSticker 方法的实现 它首先检查是否已经为 图像提取了贴纸 以便立即返回缓存的贴纸 如果贴纸还没有被缓存 它会从图像数据中提取主题 并创建一个新贴纸 extractSubject 方法会执行 开销很高的图像处理 因此我不希望阻止主 Actor 或任何其他 Actor 我可以使用 @concurrent 属性 转移这项工作 @Concurrent 可确保函数始终 在并发线程池上运行 从而释放出 Actor 来同时 执行其他任务 这些语言方面的变化通过相互配合 让并发机制更容易实现 首先编写默认在主 Actor 上 运行的代码 这时不存在数据争用风险 当你开始使用 async 函数时 这些函数会在它们被调用的 任何位置运行 这时仍不存在数据争用风险 因为你的所有代码仍在 主 Actor 上运行 当你准备好采用并发机制 来提高性能时 可以轻松将特定代码转移到后台 来并行运行 如果你想要进一步了解如何 在实践中使用并发机制 请观看“使用 Swift 并发机制 提升 App 性能”讲座 要进一步了解基本的并发概念 请观看“采用 Swift 并发”讲座 在这些语言变化中 有些是可选的 因为它们要求你更改项目才能适配 要查找和启用所有与并发机制有关的 适用语言变化 请访问 Xcode 构建设置的 “Swift Compiler - Concurrency”部分 你还可以使用 SwiftSettings API 在 Swift 软件包中启用这些功能 Swift 6.2 提供了迁移工具 来帮助你自动完成必要的代码更改 你可以访问 swift.org/migration 来进一步了解迁移工具 这些并发方面的改进是根据你 就如何在项目中采用数据安全机制 提出的反馈而打造的 无论你是报告了错误、 就某个不理解的并发错误 提出了问题、 在论坛上查看了改进建议 还是亲自编写了语言方面的更改 你的意见都至关重要 感谢你为了让每个人更容易上手使用 并发机制而给予的帮助 如果你想参与改进 Swift 请前往 swift.org 加入我们的论坛 欢迎所有人参与推动这个语言和 生态系统的不断向前发展 我们非常期待能够 在社区展示部分中听到 你自己的项目开发体验 你还可以通过论坛来及时了解 与 Swift 有关的一切 包括会议、聚会、社交媒体等等 感谢你今天的参与 期待在论坛上与你沟通交流
-
-
9:44 - Subprocess: Call `run` with string
import Subprocess let result = try await run( .name("pwd") )
-
10:04 - Subprocess: Call `run` with file path
import Subprocess let swiftPath = FilePath("/usr/bin/swift") let result = try await run( .path(swiftPath), arguments: ["--version"] )
-
10:05 - Subprocess: Accessing standard output
import Subprocess let swiftPath = FilePath("/usr/bin/swift") let result = try await run( .path(swiftPath), arguments: ["--version"] ) let swiftVersion = result.standardOutput
-
10:51 - NotificationCenter: Dynamic types
import UIKit @MainActor class KeyboardObserver { func registerObserver(screen: UIScreen) { let center = NotificationCenter.default let token = center.addObserver( forName: UIResponder.keyboardWillShowNotification, object: screen, queue: .main ) { notification in guard let userInfo = notification.userInfo else { return } let startFrame = userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as? CGRect let endFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect guard let startFrame, let endFrame else { return } self.keyboardWillShow(startFrame: startFrame, endFrame: endFrame) } } func keyboardWillShow(startFrame: CGRect, endFrame: CGRect) {} }
-
11:34 - NotificationCenter: Concrete types
import UIKit @MainActor class KeyboardObserver { func registerObserver(screen: UIScreen) { let center = NotificationCenter.default let token = center.addObserver( of: screen, for: .keyboardWillShow ) { keyboardState in let startFrame = keyboardState.startFrame let endFrame = keyboardState.endFrame self.keyboardWillShow(startFrame: startFrame, endFrame: endFrame) } } func keyboardWillShow(startFrame: CGRect, endFrame: CGRect) {} }
-
12:01 - NotificationCenter: Conformances
extension UIResponder { public struct KeyboardWillShowMessage: NotificationCenter.MainActorMessage } extension HTTPCookieStorage { public struct CookiesChangedMessage: NotificationCenter.AsyncMessage }
-
12:48 - Observation: The @Observable macro
import Observation enum Item { case none case banana case star } @Observable class Player { let name: String var score: Int = 0 var item: Item = .none init(name: String) { self.name = name } }
-
12:58 - Observation: The Observations type
import Observation enum Item { case none case banana case star } @Observable class Player { let name: String var score: Int = 0 var item: Item = .none init(name: String) { self.name = name } } let player = Player(name: "Holly") let values = Observations { let score = "\(player.score) points" let item = switch player.item { case .none: "no item" case .banana: "a banana" case .star: "a star" } return "\(score) and \(item)" }
-
13:56 - Observation: Transactional updates
import Observation enum Item { case none case banana case star } @Observable class Player { let name: String var score: Int = 0 var item: Item = .none init(name: String) { self.name = name } } let player = Player(name: "Holly") let values = Observations { let score = "\(player.score) points" let item = switch player.item { case .none: "no item" case .banana: "a banana" case .star: "a star" } return "\(score) and \(item)" } player.score += 2 player.item = .banana
-
14:05 - Observation: AsyncSequence
import Observation enum Item { case none case banana case star } @Observable class Player { let name: String var score: Int = 0 var item: Item = .none init(name: String) { self.name = name } } let player = Player(name: "Holly") let values = Observations { let score = "\(player.score) points" let item = switch player.item { case .none: "no item" case .banana: "a banana" case .star: "a star" } return "\(score) and \(item)" } player.score += 2 player.item = .banana for await value in values { print(value) }
-
14:17 - Swift Testing
import Testing import Foundation import EvolutionMetadataModel @Test func validateProposalID() async throws { let (data, _) = try await URLSession.shared.data(from: evolutionJSONMetadataURL) let jsonDecoder = JSONDecoder() let metadata = try jsonDecoder.decode(EvolutionMetadata.self, from: data) for proposal in metadata.proposals { #expect(proposal.id.starts(with: "SE")) } }
-
14:54 - Swift Testing: Attachments
import Testing import Foundation import EvolutionMetadataModel @Test func validateProposalID() async throws { let (data, _) = try await URLSession.shared.data(from: evolutionJSONMetadataURL) Attachment.record(data, named: "evolution-metadata.json") let jsonDecoder = JSONDecoder() let metadata = try jsonDecoder.decode(EvolutionMetadata.self, from: data) for proposal in metadata.proposals { #expect(proposal.id.starts(with: "SE")) } }
-
15:23 - Exit Tests: Preconditions
extension Proposal { public var number: Int { let components = id.split(separator: "-") precondition( components.count == 2 && components[1].allSatisfy(\.isNumber), "Invalid proposal ID format \(id); expected SE-<Number>" ) return Int(components[1])! } }
-
15:34 - Exit Tests: processExitsWith argument
import Testing import EvolutionMetadataModel @Test func invalidProposalPrefix() async throws { await #expect(processExitsWith: .failure) { let proposal = Proposal(id: "SE-NNNN") _ = proposal.number } }
-
31:06 - Concurrency: Async function error message
class PhotoProcessor { func extractSticker(data: Data, with id: String?) async -> Sticker? { } } @MainActor final class StickerModel { let photoProcessor = PhotoProcessor() func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? { guard let data = try await item.loadTransferable(type: Data.self) else { return nil } return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier) } }
-
32:06 - Concurrency: Run async functions on the caller's actor
// Run async functions on the caller's actor class PhotoProcessor { func extractSticker(data: Data, with id: String?) async -> Sticker? {} } @MainActor final class StickerModel { let photoProcessor = PhotoProcessor() func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? { guard let data = try await item.loadTransferable(type: Data.self) else { return nil } return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier) } }
-
32:36 - Concurrency: Conformance error
protocol Exportable { func export() } extension StickerModel: Exportable { // error: Conformance of 'StickerModel' to protocol 'Exportable' crosses into main actor-isolated code and can cause data races func export() { photoProcessor.exportAsPNG() } }
-
33:04 - Concurrency: Isolated conformances
// Isolated conformances protocol Exportable { func export() } extension StickerModel: @MainActor Exportable { func export() { photoProcessor.exportAsPNG() } }
-
33:20 - Concurrency: Isolated conformance use
// Isolated conformances @MainActor struct ImageExporter { var items: [any Exportable] mutating func add(_ item: StickerModel) { items.append(item) } func exportAll() { for item in items { item.export() } } }
-
33:31 - Concurrency: Isolated conformance error
// Isolated conformances nonisolated struct ImageExporter { var items: [any Exportable] mutating func add(_ item: StickerModel) { items.append(item) // error: Main actor-isolated conformance of 'StickerModel' to 'Exportable' cannot be used in nonisolated context } func exportAll() { for item in items { item.export() } } }
-
33:51 - Concurrency: Unsafe static variable
final class StickerLibrary { static let shared: StickerLibrary = .init() // error: Static property 'shared' is not concurrency-safe because non-'Sendable' type 'StickerLibrary' may have shared mutable state }
-
34:01 - Concurrency: Protecting static variables
final class StickerLibrary { @MainActor static let shared: StickerLibrary = .init() }
-
34:05 - Concurrency: Protecting classes
@MainActor final class StickerLibrary { static let shared: StickerLibrary = .init() }
-
34:15 - Concurrency: A single-threaded program
@MainActor final class StickerLibrary { static let shared: StickerLibrary = .init() } @MainActor final class StickerModel { let photoProcessor: PhotoProcessor var selection: [PhotosPickerItem] } extension StickerModel: @MainActor Exportable { func export() { photoProcessor.exportAsPNG() } }
-
34:22 - Concurrency: Mode to infer main actor by default
// Mode to infer main actor by default final class StickerLibrary { static let shared: StickerLibrary = .init() } final class StickerModel { let photoProcessor: PhotoProcessor var selection: [PhotosPickerItem] } extension StickerModel: Exportable { func export() { photoProcessor.exportAsPNG() } }
-
35:06 - Concurrency: Explicitly offloading async work
// Explicitly offloading async work class PhotoProcessor { var cachedStickers: [String: Sticker] func extractSticker(data: Data, with id: String) async -> Sticker { if let sticker = cachedStickers[id] { return sticker } let sticker = await Self.extractSubject(from: data) cachedStickers[id] = sticker return sticker } @concurrent static func extractSubject(from data: Data) async -> Sticker {} }
-
-
- 0:00 - Introduction & Agenda
Learn what's new in Swift 6.2. Highlights include: workflow enhancements, new library APIs, expanded adoption across the software stack, and improved concurrency language features for more productive coding.
- 0:48 - swiftlang updates
The swiftlang organization on GitHub has grown significantly, encompassing over 50 projects, including the Swift compiler, Xcode’s build system Swift Build, and the Swift.org website. Swift Build, now open source, supports the build process for Apple operating systems, and is adopted by the Swift Package Manager. A new version manager, swiftly, originally developed for Linux, now supports macOS and simplifies Swift toolchain management. The redesigned Swift.org homepage can also help you dive into different areas.
- 3:06 - Development workflow: Writing code
The latest Swift release enhances development workflows across all platforms, with specific improvements for VS Code Swift extension users including official verification and distribution of the Swift extension by Swift.org. Now, there's also background indexing for real-time editor features, enhanced code completion, simplified debugging with automatic LLDB support, and new project panel and live DocC previews.
- 4:40 - Development workflow: Building
Swift 6.2 introduces several enhancements to boost your productivity. Clean build times for projects utilizing macro-based APIs are significantly improved by eliminating the need to build the swift-syntax library during clean builds. This optimization, supported by Swift PM and Xcode, can reduce build times by minutes. This release also enhances compiler diagnostics documentation by providing more detailed explanations of common warnings and errors, making them easier to understand and resolve. Additionally, you now have greater control over compiler warnings; you can specify which warnings to treat as errors, a feature driven by community feedback.
- 7:36 - Development workflow: Debugging
Debugging is also enhanced in Swift 6.2, particularly for asynchronous code, with improved LLDB functionality, task naming and visibility in Instruments profiles. It also has faster debugger response times due to explicitly built modules enabled by default in Xcode 26.
- 9:14 - Libraries: Subprocess
This release introduces the new Subprocess package, with which you can launch and manage subprocesses directly from Swift code. The package gives you fine-grained control over process execution, so you can specify the executable path. When the subprocess terminates, you can inspect the exit status, standard output, and other information about the process execution, with version 0.1 now available and ready for feedback.
- 10:45 - Libraries: Foundation
The Foundation Workgroup enhanced iOS app development by introducing concrete types for notification names and payloads, streamlining code, eliminating errors, and improving thread safety for both framework and custom notifications. You can also add concrete notification types for your own notifications.
- 12:31 - Libraries: Observation
The Observation Library in Swift uses the observer pattern to track state changes in an object graph. The 'Observable' macro enables observation tracking. Swift 6.2 introduces the 'Observations' type, with which you can create an 'AsyncSequence' to stream state changes based on a closure that computes a value from observable properties. Updates are transactional, ensuring consistent state, and you can iterate over them using a for-await loop.
- 14:13 - Libraries: Testing
Using Swift Testing, a cross-platform library, you can write and organize tests by using macros. Swift 6.2 enhances this capability with custom attachments for better failure diagnosis, especially in remote environments, and exit tests to validate code that terminates under specific conditions. These features support testing portable Swift code, beyond just your application code.
- 16:08 - Swift throughout the stack: Embedded Swift
Swift 6.2 enhances Embedded Swift, enabling you to write code for embedded devices, servers, and security-critical components. It now includes full-string APIs, Swift's 'any' types for class-constrained protocols, and new APIs like 'InlineArray' and 'Span' for working efficiently with regions of memory. Apple uses Embedded Swift in some of the lowest-level software on iPhone, and the community created examples available on GitHub in the swift-embedded-examples repository.
- 18:00 - Swift throughout the stack: Security
Swift 6.2 introduces strict-memory-safety mode, an opt-in feature that requires explicit acknowledgment of unsafe API uses, aiding in identifying security-critical code sections. Apple is adopting this mode in WebKit and a Messages app subsystem, which both handle untrusted input.
- 19:37 - Swift throughout the stack: Server
Swift is widely used in the server ecosystem, particularly at Apple, where it powers backend services processing millions of requests per second. A notable example is a password alert service, previously built in Java, which saw a 40% increase in throughput and a 50% reduction in hardware requirements after being rewritten in Swift. Other companies, such as Cultured Code, also benefited significantly from adopting Swift. The Things Cloud backend, reimplemented in Swift, experienced a 3x reduction in compute costs and a 400% improvement in average response times. Swift's growing package ecosystem, interoperability with C, Objective-C, and C++, and new open source projects like swift-java and a containerization library, enable developers to build efficient, performant server backends, and seamlessly integrate Swift with existing codebases, particularly in Java.
- 23:23 - Swift throughout the stack: Platforms
Swift 6.2 now officially supports FreeBSD and WebAssembly (Wasm), enabling you to build client and server applications for browsers and other runtimes. The Wasm support, which started as a community project, allows you to compile Swift code and run it in the browser, as demonstrated by a 3D rendering app using WebGPU and JavaScriptKit. Swift's safety, ease of use, and performance make it an attractive choice throughout the software stack.
- 26:11 - Language evolution: Performance
The new release also introduces two new types, 'InlineArray' and 'Span', to enhance performance of critical code. 'InlineArray' is a fixed-size array that stores elements directly, eliminating the need for heap allocation. This improves performance, especially in hot paths, and enables more optimizations like eliminating bounds checking. The system specifies the size of an 'InlineArray' as part of the type, and can infer it from array literals. 'Span' provides fast, direct access to contiguous memory without compromising memory safety. It allows functions to operate over the underlying storage of various container types, such as 'Array' and 'InlineArray', safely and efficiently. 'Span' ensures memory validity through compile-time checks, preventing common memory safety issues inherent to pointers.
- 30:28 - Language evolution: Concurrency
Concurrency is a challenging aspect of programming due to the potential for data races when multiple tasks share memory. Swift 6 introduced data-race safety at compile time, but this often led to compiler errors when because the language would offload work to the background implicitly, even if you never needed the code to run in parallel. Swift 6.2 addresses this issue by changing the default philosophy to stay single-threaded until the you explicitly introduce concurrency. This capability, by default, makes the most natural code to write data race-free. The language now allows async functions that aren't tied to a particular actor to continue in the same actor it was called from, eliminating data races. The release also introduces isolated conformances, enabling main actor types to conform to protocols while ensuring the compiler protects main actor state. Additionally, there's now an opt-in mode to infer the main actor by default, reducing concurrency annotations in mostly single-threaded code. When you need concurrency for performance improvement, such as offloading CPU-intensive tasks to the background, Swift 6.2 provides tools to do so safely. The concurrent attribute ensures that specific functions run on the concurrent thread pool, freeing up the current actor to perform tasks simultaneously. These changes work together to make concurrency more approachable and easier to implement in Swift projects.
- 37:15 - Wrap up
User feedback led to Swift's concurrency improvements, making it more user friendly. The community is encouraged to participate in the forums at Swift.org to continue language and ecosystem development, share projects, and stay updated on Swift-related events.