2

参考官方教程,swift中存在内存冲突,但是,根据我的javascript知识,没有内存冲突,下面的代码永远是对的。

func balance(_ x: inout Int, _ y: inout Int) {
  let sum = x + y
  x = sum / 2
  y = sum - x
}
var playerOneScore = 42
var playerTwoScore = 30
balance(&playerOneScore, &playerTwoScore)  // OK
balance(&playerOneScore, &playerOneScore)  // Error: conflicting accesses to playerOneScore
4

1 回答 1

5

内存安全

具体来说,如果您有两个满足以下所有条件的访问权限,则会发生冲突:

  • 至少一个是写访问或非原子访问。
  • 它们访问内存中的相同位置。
  • 它们的持续时间重叠。

然后:

函数对其所有输入输出参数具有长期写入权限。对 in-out 参数的写访问在所有非 in-out 参数被评估后开始,并持续到该函数调用的整个持续时间。

因此,将与 in-out 参数相同的变量传递给

func balance(_ x: inout Int, _ y: inout Int) 

算作对同一内存位置的重叠写访问,因此算作冲突。

有关基本原理和更多详细信息,请参阅SE-0176 Enforce Exclusive Access to Memory,它已在 Swift 4 中实现。特别是,强制独占内存访问

  • 防止未定义的行为和意外或令人困惑的结果。
  • 允许编译器对加载和存储做出乐观的假设。

例如,如果将相同的变量作为 inout 参数传递,则以下两个看似等效的函数实际上是不等效的,因此变异x可以影响y,反之亦然:

func balance(_ x: inout Int, _ y: inout Int) {
    let sum = x + y
    x = sum / 2
    y = sum - x
}

func balance(_ x: inout Int, _ y: inout Int) {
    x = (x + y) / 2
    y = (x + y) / 2 - x
}

另一个例子:

var global = 0
func foo(_ x: inout Int, _ y: inout Int) {
    x += y
    global = y
}

如果变异x可能会修改y,反之亦然,编译器无法优化代码以首先将值加载到y寄存器中,即执行相当于

func foo(_ x: inout Int, _ y: inout Int) {
    let savedY = y
    x += savedY
    global = savedY
}

还讨论了(参见消除非瞬时访问?)通过在函数调用期间制作临时副本来消除长期访问,这些临时副本在函数返回时分配回,即执行类似的操作

func balance(_ x: inout Int, _ y: inout Int) {
    var (localX, localY) = (x, y)
    let sum = localX + localY
    localX = sum / 2
    localY = sum - localX
    (x, y) = (localX, localY)
}

这个想法被抛弃了,因为它不利于性能,即使对于“简单类型”也是如此,但对于像Array.

于 2020-12-24T15:16:12.093 回答