默认启用完整并发
在 Swift 6 之前,设置 Xcode-Build Settings- Strict Concurrency Checking 为 Complete,启用完整并发检查
调用 loadData()
会抛出一个警告:
passing argument of non-sendable type 'User' outside of main actor-isolated context may introduce data races.
意思就是在主隔离上下文以外传递不可发送参数,可能会导致数据竞争
在 Swift 6 之后,这个警告消失了:Swift 现在检测到代码实际上没有问题,因为 user
并没有被同时访问,因此不会发出警告。
全局变量的并发安全
要求全局变量和类型中的静态变量在并发环境中是安全的
var test = 0
struct Test1 {
static var test = true
}
struct Test2 {
static let test = 9.975
}
以上代码在 Swift6 之前打开完整并发检查会警告 test 和 Test1.test,在 Swift6 中是错误
Var 'test' is not concurrency-safe because it is non-isolated global shared mutable state; this is an error in Swift 6
我们可以通过改成 let 修饰 或者添加 @MainActor 隔离在主线程调用
还有一种方法可以解除报错,标记为非隔离的
// 不推荐,除非你确定它是安全的
nonisolated(unsafe) var test = 0
函数默认值的隔离
以下代码在 Swift6 之前会报错(未开启完整并发检查的情况下),在 Swift6 之后正常编译
@MainActor
class Logger {
}
@MainActor
class DataController {
init(logger: Logger = Logger()) {
}
}
// error
Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
新增 count(where:)
引入了一个新的 count(where:)
方法,它相当于先 filter()
再 count
的操作。直接使用这个方法可以少创建一个新数组
// 2 步操作
[1, 2, 3, -1, -2].filter({ $0 > 0 }).count // => 3
// 1 步到位
[1, 2, 3, -1, -2].count(where: { $0 > 0 }) // => 3
PR 实现
@inlinable
public func count(
where predicate: (Element) throws -> Bool
) rethrows -> Int {
var count = 0
for e in self {
if try predicate(e) {
count += 1
}
}
return count
}
}
类型化的 throws
enum TestError: Error {
case test1, test2
}
// Swift 5.x
func testError1(_ num: Int) throws {
if num > 5 {
throw TestError.test1
}
}
// Swift 6
func testError2(_ num: Int) throws(TestError) {
if num > 5 {
throw .test2
}
}
// 调用
do {
try testError1(10)
} catch {
debugPrint(error)
}
在 Swift6 中如果指定了抛出的 error 类型,那么不可在抛出其他类型的 error
Pack(参数包) 迭代
Swift 5.9 提案地址 [SE-0393] 中引入的参数包功能的能力,可以说是 Swift 中很复杂的功能之一
比如比较元组是否相等
struct NotEqual: Error {}
func == <each Element: Equatable>(lhs: (repeat each Element), rhs: (repeat each Element)) -> Bool {
// Local throwing function for operating over each element of a pack expansion.
func isEqual<T: Equatable>(_ left: T, _ right: T) throws {
if left == right {
return
}
throw NotEqual()
}
// Do-catch statement for short-circuiting as soon as two tuple elements are not equal.
do {
repeat try isEqual(each lhs, each rhs)
} catch {
return false
}
return true
}
// 定义元组,每个元组包含三个元素
let tuple1 = (1, "hello", 3.14)
let tuple2 = (1, "hello", 3.14)
let tuple3 = (2, "world", 2.71)
// 使用我们定义的相等操作符进行比较
let isEqual1 = tuple1 == tuple2 // 这将返回 true,因为两个元组的所有元素都相等
let isEqual2 = tuple1 == tuple3 // 这将返回 false,因为元组的元素不完全相同
print(isEqual1) // 输出: true
print(isEqual2) // 输出: false
Swift 6 新的提案优化了 pack 技术,可以使用for-in
循环对值包进行迭代
func == <each Element: Equatable>(lhs: (repeat each Element), rhs: (repeat each Element)) -> Bool {
for (left, right) in repeat (each lhs, each rhs) {
guard left == right else { return false }
}
return true
}
非连续元素的集合操作
我们可以使用 Range<Index>
来引用集合中的一组连续位置,但标准库目前不提供引用任意集合中不连续位置的方法,所以有了这么一个提案 RangeSet<Bound: Comparable>
var numbers = Array(1...15)
// Find the indices of all the even numbers
let indicesOfEvens = numbers.indices(where: { $0.isMultiple(of: 2) })
// Perform an operation with just the even numbers
let sumOfEvens = numbers[indicesOfEvens].reduce(0, +)
// sumOfEvens == 56
// You can gather the even numbers at the beginning
let rangeOfEvens = numbers.moveSubranges(indicesOfEvens, to: numbers.startIndex)
debugPrint(numbers)
debugPrint(numbers[rangeOfEvens])
debugPrint(numbers[indicesOfEvens])
导入声明的访问级别修饰符
增加了为导入声明标记访问控制修饰符的能力。
比如以后导入头文件你可以这么写了 internal import SomeLibrary
。作用是 SomeLibrary 这个库只会在你的库内部使用,外部引入你的库的 App 看不到这个依赖库。
这将对开发 SDK 的团队避免意外泄露其依赖关系非常有用。
不可复制类型的升级
不可复制类型是 Swift5.9 引入的 提案地址 [SE-0390],但是不支持泛型、协议,Swift6 支持了 结构体、枚举、类、泛型参数、协议和关联类型
struct Test: ~Copyable {
consuming func test() {
debugPrint("test")
}
deinit {
debugPrint("deinit")
}
}
let test = Test()
test.test()
// 放开注释就会报错 'test' consumed more than once,因为已经被消耗了
// test.test()
128 位整数类型
let num: Int128 = 170_141_183_460_469_231_731_687_303_715_884_105_727
函数体宏
@Logged
func g(a: Int, b: Int) -> Int {
return a + b
}
func g(a: Int, b: Int) -> Int {
log("Entering g(a: (a), b: (b))")
defer {
log("Exiting g")
}
return a + b
}