同时读写一个存储地址,会引发访问冲突。
重叠访问主要出现在:
- 使用in-out参数的函数和方法
- 结构体的mutating方法里
in-out参数的访问冲突
- 变量传入inout参数的方法之后,调用方法的同时又访问了传入的变量
var stepSize = 1
func increment(_ number: inout Int) {
number += stepSize
}
func test() {
// 复制一份
// 否则报错:Simultaneous accesses to 0x7fda39420ca0, but modification requires exclusive access.
var copyStepSize = stepSize
increment(©StepSize)
}
- 同一个变量传入了含有多个inout参数的方法里,造成同时访问的情况
func balance(_ x: inout Int, _ y: inout Int) {
let sum = x + y
x = sum / 2
y = sum - x
}
func test() {
var playerOneScore = 42
var playerTwoScore = 30
balance(&playerOneScore, &playerTwoScore) // 正常
// copy之后
var copyPlayerOneScore = playerOneScore
var copyPlayerTwoScore = playerOneScore
balance(©PlayerOneScore, ©PlayerTwoScore)
print(copyPlayerOneScore)
// 错误:playerOneScore 访问冲突
}
- 结构体的mutating方法
对self的写操作会从mutating方法的开始到结束。
mutating 方法在调用期间需要对 self 发起写访问,而同时 in-out 参数也需要写访问。
属性的访问冲突
任何情况下,对于元组元素的写访问都需要对整个元组发起写访问。这意味着对于 playerInfomation 发起的两个写访问重叠了,造成冲突。
使用inout参数同时传入元组的2个元素的话,就会同时访问元组引起冲突。
限制结构体属性的重叠访问对于内存安全并不总是必要的。内存安全是必要的,但访问独占权的要求比内存安全还要更严格 —— 意味着即使有些代码违反了访问独占权的原则,也是内存安全的。如果编译器可以保证这种非专属的访问是安全的,那 Swift 就会允许这种内存安全的行为。特别是当你遵循下面的原则时,它可以保证结构体属性的重叠访问是安全的:
- 你访问的是实例的存储属性,而不是计算属性或类的属性
- 结构体是本地变量的值,而非全局变量
- 结构体要么没有被闭包捕获,要么只被非逃逸闭包捕获了
如果编译器无法保证访问的安全性,它就不会允许访问。