我一直在学习swift,遇到了一个关于内存安全的问题。 +=
运算符采用左侧的 inout
参数,该参数应对整个函数调用具有写访问权限。它在其实现中执行类似 left = right+left
的操作。这似乎是写入和读取访问的重叠。这怎么不违反内存安全呢?
编辑: 根据The Swift Programming Language ,它可以发生在单个线程中:
However, the conflicting access discussed here can happen on a single thread and doesn’t involve concurrent or multi-threaded code.
详细说明:
以下是《Swift 编程语言》(Swift 4.1 beta)中的两个示例。我很困惑如何在结构 Vector2D
中实现此自定义 +=
没问题:
static func += (left: inout Vector2D, right: Vector2D) {
left = left + right
}
当这不是:
var stepSize = 1
func incrementInPlace(_ number: inout Int) {
number += stepSize
}
incrementInPlace(&stepSize)
// Error: conflicting accesses to stepSize
进一步编辑:
我认为我的问题确实是 += 作为一个函数,特别是在使用时
stepSize += stepSize
或者使用自定义实现:
var vector = Vector2D(x: 3.0, y: 1.0)
vector += vector
这没有任何错误。但是 func 从左侧获取输入,因此可以对“step”进行长期写入访问,那么如果右侧也传入“step”,我很困惑这为什么不是“step”的即时读取访问”与长期写的“步骤”重叠。或者,当您在同一个实例中传递两个 inout 参数,而不是一个 inout 和一个常规参数时,这只是一个问题吗?
最佳答案
我知道您已经明白了,但需要向 future 的读者进行澄清;在您的评论中,您说:
... it is, in the end, a problem with any one-line code changing self by reading self first.
不,仅此还不够。作为Memory Safety章节说,这个问题仅在以下情况下才会显现出来:
- At least one is a write access.
- They access the same location in memory.
- Their durations overlap.
考虑:
var foo = 41
foo = foo + 1
foo = foo + 1
不是问题(foo += 1
也不会;foo += foo
也不会),因为构成一系列“瞬时”访问。因此,尽管我们有(用你的话来说)“通过首先读取 self 来更改 self 的代码”,但这不是问题,因为它们的持续时间不重叠。
只有当您处理“长期”访问时,问题才会显现出来。那样guide继续说道:
A function has long-term write access to all of its in-out parameters. The write access for an in-out parameter starts after all of the non-in-out parameters have been evaluated and lasts for the entire duration of that function call. If there are multiple in-out parameters, the write accesses start in the same order as the parameters appear.
One consequence of this long-term write access is that you can’t access the original variable that was passed as in-out, even if scoping rules and access control would otherwise permit it—any access to the original creates a conflict.
所以,考虑你的第二个例子:
var stepSize = 1
func incrementInPlace(_ number: inout Int) {
number += stepSize
}
incrementInPlace(&stepSize)
在这种情况下,您可以长期访问任何number
引用。当您使用 &stepSize
调用它时,这意味着您可以长期访问与 stepSize
关联的内存,因此 number += stepSize
意味着您正在尝试访问 stepSize
,而您已经拥有对它的长期访问权限。
关于swift - Swift 中 “+=” 运算符的内存安全,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49832784/