这里每天分享一个 iOS 的新知识,快来关注我吧
前言
随着今天 iOS 18.0 正式版的发布,Xcode 16.0 和 swift 6.0 也正式发布了,今天 Swift 语言团队的工程经理 Holly Borla 宣布了 Swift 6 的正式发布。这是一个重要的新版本,将 Swift 扩展到了更多的平台和领域。
很多人知道 Swift 是一个用于应用开发的语言,目前在 App Store 上有几百万个应用是由 Swift 开发的。但 Swift 不仅仅适用于应用程序开发。Swift 的安全性、速度和易用性使其成为许多其他用途的绝佳选择,比如服务器端开发、网络服务、甚至是嵌入式开发。
Swift 6 通过新的底层编程特性、嵌入式 Swift 语言子集、扩展的 Linux 和 Windows 支持、新的跨平台 API(包括新的 Swift 测试库)等进一步扩展了其应用范围。
之前写过一篇 Swift 6 的介绍,可以参考:Swift 6 正式发布,带来哪些新特性?今天再来唠叨唠叨。
语言和标准库
并发
Swift 长期以来提供了内存安全性,确保变量在使用前已初始化,内存在释放后不会被访问,并检查数组索引是否超出边界。Swift 6 现在包括一种新的可选语言模式,通过将数据竞态诊断为编译器错误,扩展了 Swift 的安全保证,以防止并发代码中的数据竞态。
数据竞态安全检查在 Swift 5.10 中作为警告通过 -strict-concurrency=complete
编译器标志提供。由于改进了 Sendable 推断和新的编译器分析以将可变状态从一个 actor 转移到另一个 actor,Swift 6 关于数据竞态安全的警告误报减少了。
Swift 6 还带来了一个新的同步库,用于低级并发 API,包括原子操作和新的互斥 API。
Typed throws
Swift 6 允许函数在其签名中指定它们抛出的错误类型。这一特性在转发客户端代码中抛出的错误的泛型代码中或在无法分配内存的资源受限环境(如嵌入式 Swift 代码)中非常有用。
例如:
swift
func parseRecord(from string: String) throws(ParseError) -> Record {
// 这里解析字符串
}
调用 parseRecord(from:) 将返回一个 Record 实例或抛出 ParseError 类型的错误。do..catch 代码块将推断 ParseError 作为错误变量的类型:
csharp
do {
let record = try parseRecord(from: myString)
} catch {
// 这里的 error 是 ParseError 类型
}
Typed throws 泛化了抛出和不抛出异常的函数。指定为 throws(没有特定错误类型)的函数等效于指定为 throws(any Error) 的函数,而不抛出异常的函数等效于指定为 throws(Never) 的函数。调用 throws(Never) 的函数是非抛出的,不需要在调用点进行错误处理。
Typed throws 也可以在泛型函数中用于从参数传播错误类型,比 rethrows 更精确。例如,Sequence.map 方法可以从其闭包参数传播抛出的错误类型,表明它只抛出与闭包相同类型的错误:
swift
extension Sequence {
func map<T, E>(_ body: (Element) throws(E) -> T) throws(E) -> [T] {
// 这里使用 throws(E) 表示这个函数可能抛出 E 类型的错误
}
}
当给定一个抛出 ParseError 的闭包时,map 将抛出 ParseError。当给定一个不抛出的闭包时,E 被推断为 Never,map 将不抛出。
Ownership
Swift 5.9 引入了使用 ~Copyable 语法的不可复制类型,用于建模具有唯一所有权的资源,并通过消除与复制相关的运行时开销来编写性能代码。Swift 6 现在支持这些类型与泛型系统,使得编写适用于可复制和不可复制类型的泛型代码成为可能。
例如:
scss
protocol Drinkable: ~Copyable {
consuming func use()
}
struct Coffee: Drinkable, ~Copyable { /* ... */ }
struct Water: Drinkable { /* ... */ }
func drink(item: consuming some Drinkable & ~Copyable) {
item.use()
}
drink(item: Coffee())
drink(item: Water())
Drinkable 协议没有要求其符合类型是可复制的。这意味着不可复制类型 Coffee 和可复制类型 Water 都可以传递给泛型 drink 函数。
Switch 语句现在可以避免在枚举模式匹配操作中复制。这意味着 switch 语句可以与不可复制一起使用,并且还可以为基于写时复制容器(如 Array 和 Dictionary)的可复制提供性能优势。
不可复制类型已经在整个标准库中使用。例如,上边提到的同步库中的新 Atomic 类型基于 ~Copyable,Optional 和 Result 现在可以包装不可复制类型,不安全的缓冲指针类型现在可以指向不可复制的元素。C++ 互操作性也使用不可复制类型将 C++ 仅移动类型暴露给 Swift。
C++ 互操作性
Swift 5.9 就开始引入了与 C++ 的双向互操作性,使 Swift 能够无缝地引入更多现有项目。Swift 6 扩展了对 C++ 移动仅类型、虚方法、默认参数以及更多标准库类型(包括 std::map 和 std::optional)的互操作性支持。
没有复制构造函数的 C++ 类型现在可以从 Swift 6 作为不可复制类型使用 ~Copyable。而当将 C++ 类型暴露为 ~Copyable 在 Swift 中具有更好性能时,可以在 C++ 类型上应用新的 SWIFT_NONCOPYABLE 注释。
当调用具有一些参数默认值的 C++ 函数或方法时,Swift 现在支持这些默认值,而不是要求你显式传递参数。
嵌入式 Swift
Swift 6 包括嵌入式 Swift 的预览,这是一个适用于嵌入式软件开发的语言子集和编译模式,例如编程微控制器。工具链支持 ARM 和 RISC-V 裸机目标。
嵌入式 Swift 通过依赖于泛型特化来生成小型独立的二进制文件。由于它不依赖于运行时或类型元数据,嵌入式 Swift 适用于内存限制紧张的平台以及运行时依赖有限的低级环境。
嵌入式 Swift 仍然是一个实验性特性,正在进行开发,以在未来的 Swift 版本中提供稳定支持。
128 位整数
Swift 6 补全了整数集合,增加了有符号和无符号的 128 位整数类型。这些类型在所有 Swift 平台上可用,并提供与标准库中其他整数类型相同的 API。
生产力增强
Swift 6 引入了许多能够提高生产力的功能,包括:
-
count(where:)
来简化统计满足谓词的序列元素数量 -
pack
迭代以自然地编写值参数包中的元素循环 -
导入的访问控制以防止实现细节泄露到公共 API 中
-
@attached(body)
宏用于合成和增强函数实现 -
表达式宏作为默认参数
调试
使用 @DebugDescription 的自定义 LLDB 摘要
Swift 6 提供了一个新的调试宏,可以轻松自定义对象在使用 p 命令时在 LLDB 中的显示方式,以及在 Xcode 和 VSCode 中的变量视图中,使用一种不运行任意代码的格式化方案。
符合 CustomDebugStringConvertible 的类型提供一个 debugDescription 属性,该属性返回描述对象的字符串。在 LLDB 中,po 命令调用对象上的这个计算属性。相比之下,p 命令使用 LLDB 的类型摘要格式化程序直接使用其存储的属性值格式化对象。
@DebugDescription
是标准库中的一个新宏,允许你直接在代码中为自己的类型指定 LLDB 类型摘要。该宏处理 debugDescription 属性,将涉及存储属性的简单字符串插值转换为 LLDB 类型摘要。这样,LLDB 可以在使用 p 命令时使用你的自定义格式化,并且在 Xcode 或 VSCode 的变量显示窗口中也是如此。该宏可以使用现有的 CustomDebugStringConvertible 符合性,或者你可以仅为 LLDB 的 p 命令提供单独的字符串插值。如果你的 CustomDebugStringConvertible 实现不满足 @DebugDescription 宏的要求,或者你熟悉 LLDB 摘要字符串语法并希望直接使用它,提供单独的 LLDB 描述字符串是有用的。
例如,以下代码自定义了 LLDB 中使用 po 显示 Organization 类型的方式,符合 CustomDebugStringConvertible,并且 @DebugDescription 宏将此自定义格式化暴露给 p 命令和变量视图:
less
@DebugDescription
struct Organization: CustomDebugStringConvertible {
var id: String
var name: String
var manager: Person
// ... 还有更多
var debugDescription: String {
"#\(id) \(name) [\(manager.name)]"
}
}
(lldb) p myOrg
(Organization) myOrg = "`#100 Worldwide Travel [Jonathan Swift]`"
使用显式模块改进启动性能
Swift 6 在调试器中使用显式模块构建时显著改进了启动性能。当调试本地构建的代码时,LLDB 现在可以直接从项目构建工件中导入显式构建的 Swift 和 Clang 模块。这避免了从源代码重新编译隐式 Clang 模块依赖项的需要,这可能需要很长时间,并且对头文件搜索路径的问题非常敏感。
如果 LLDB 中的第一个 p 或 po 命令由于 Clang 模块编译而需要很长时间,或者你的调试经常被 Clang 头文件导入问题阻塞,请考虑在你的项目中使用显式模块来加快调试速度!
库
Foundation
Swift 6 统一了各个平台上 Foundation 的实现。现代、可移植的 Swift 实现提供了一致性、更加健壮,并且是开源的。macOS 和 iOS 从 Swift 5.9 开始使用 Swift 实现的 Foundation,Swift 6 将这些改进带到了 Linux 和 Windows。
核心类型如 JSONDecoder、URL、Calendar、FileManager、ProcessInfo 等已完全在 Swift 中重新实现。这些类型与 macOS 15 和 iOS 18 共享其实现,提供了新的跨平台一致性、可靠性和性能。最近发布的 API,如 FormatStyle、ParseStrategy、Predicate 和 JSON5,从过去的 macOS 和 iOS 版本中现在在所有 Swift 平台上可用。
这部分之前的文章中也有介绍过,可以参考:
Apple 宣布用 Swift 重写 Foundation 库
新的 Foundation API,如 Expression、日历枚举改进、日历重复规则、格式样式增强等,同时在 macOS、iOS、Linux 和 Windows 上可用,并且是与社区合作构建的。
如果你的 Linux 或 Windows 应用程序今天从 Swift 工具链中导入 Foundation 库,你将获得所有这些改进。如果你的应用程序对二进制大小特别敏感,你现在可以导入 FoundationEssentials 库,该库提供了 Foundation 功能的更有针对性的子集,省略了国际化和本地化数据,包大小减少了不少。
Swift Testing
Swift 6 引入了 Swift Testing,这是一个从头开始为 Swift 设计的新测试库。
它包括表达性强的 API,使编写和组织测试变得容易。它使用类似 #expect 的宏在测试失败时提供详细的输出。它通过参数化等功能扩展到大型代码库,以轻松重复使用不同参数进行测试。
例如:
swift
@Test("视频中提到的大陆", arguments: [
"A Beach",
"By the Lake",
"Camping in the Woods"
])
func mentionedContinents(videoName: String) async throws {
let videoLibrary = try await VideoLibrary()
let video = try #require(await videoLibrary.video(named: videoName))
#expect(video.mentionedContinents.count <= 3)
}
Swift Testing 充分利用了宏。其 @Test 和 @Suite 附加宏分别声明测试函数和套件类型,并接受参数(称为特性)以定制各种行为。#expect 和 #require 表达式宏验证预期行为,并捕获表达式及其子值的丰富表示,以生成详细的失败消息。
由于 Swift Testing 直接包含在 Swift 6 工具链中,你可以导入 Testing 而无需声明包依赖性。这意味着你的测试不需要构建 Swift Testing 或其依赖项(包括 swift-syntax),其宏实现已预构建。Swift 6 中的包管理器自动构建和运行 Swift Testing 测试,以及 XCTests(如果存在),并在日志输出中显示来自两个库的结果。Swift Testing 支持 Swift 官方支持的所有平台,包括所有 Apple 平台、Linux 和 Windows。
这部分内容我们有时间再详细介绍。
总结
Swift 6 这个超大的版本提供了各种各样的新特性,在其他平台和设备上使用 Swift 开发应用变得更加容易和高效。但同时,Swift 6 的入门门槛也变高了,需要开发者投入更多的时间去学习和适应新的特性和语法。
大家觉得 Swift 6 怎么样呢?欢迎在评论区留言讨论。
这里每天分享一个 iOS 的新知识,快来关注我吧
参考资料
[1]
新的同步库: developer.apple.com/documentati...
本文同步自微信公众号 "iOS新知",每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!