概览
在 Swift 的结构化并发代码中,编译器经常会报告错误:mutable capture of 'inout' parameter 'self' is not allowed in concurrently-executing code。然而,在某些特定情况下,这一错误实际上并不会导致问题。那么,当这些“伪”错误出现时,我们该如何让编译器满意并确保代码顺利执行呢?
本文将介绍以下内容:
- 1. 编译器恼人的抱怨
- 2. 什么情况下这不是一个问题?
- 3. 规避之道
- 4. 实际示例
让我们开始吧!
1. 编译器恼人的抱怨
首先,查看引发问题的代码:
struct Foo {
var name = ""
private func spawnRandNameNumber() -> Int {
Int.random(in: 0...10000)
}
mutating func dealName() async {
await withTaskGroup(of: Void.self) { group in
group.addTask {
name += "\(spawnRandNameNumber())"
}
group.addTask {
name += "\(spawnRandNameNumber())"
}
}
}
}
在这段代码中,我们使用结构化并发中的任务组(TaskGroup)尝试同时修改对象的 name 属性。编译时,编译器会在每处修改 name 属性的代码行报错,指出不允许在并发执行代码中可变捕获 inout 参数 self。
根本原因在于,我们试图并发修改 self 中同一内存位置的值,而 self 此时被 inout 修饰。因此,编译器的抱怨是合理且有依据的。但这是否在所有情况下都是错误呢?并非如此。
2. 什么情况下这不是一个问题?
假设我们尝试同时修改 self 中两个完全独立的不同属性呢?下面稍作修改:
struct Foo {
var name = ""
var age = 11
private func spawnRandNameNumber() -> Int {
Int.random(in: 0...10000)
}
private func spawnRandAge() -> Int {
Int.random(in: 1...121)
}
mutating func dealName() async {
await withTaskGroup(of: Void.self) { group in
group.addTask {
name += "\(spawnRandNameNumber())"
}
group.addTask {
age = spawnRandAge()
}
}
}
}