Swift中“+=”运算符的内存安全
Memory safety of “+=” operator in Swift
我一直在学习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 测试版)中的两个示例。 我很困惑这个自定义 +=
在结构 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 从左边获取一个 inout,因此对“step”有一个长期的写访问权限,然后如果右边也传入了“step”,我很困惑这怎么不是对“step”的即时读取访问”与“step”的长期写作重叠。还是仅当您为两个 inout 参数传递同一实例而不是一个 inout 和一个常规参数时才会出现问题?
写的时候
x +=5
等于
x = x + 5
首先对变量 x 进行读取操作,然后将值添加到 5,最后对结果进行写入操作,所有这些都是通过寄存器同步发生的,而不是同时发生
我知道你已经明白了,但要为未来的读者澄清一下;在您的评论中,您说:
... 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
也不会),因为它构成了一系列 "instantaneous" 访问。因此,尽管我们有(使用您的短语)"code changing self by reading self first",但这不是问题,因为它们的持续时间不重叠。
只有当您处理 "long-term" 访问时,问题才会显现出来。 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,遇到一个关于内存安全的问题。 +=
运算符在左侧接受一个 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 测试版)中的两个示例。 我很困惑这个自定义 +=
在结构 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 从左边获取一个 inout,因此对“step”有一个长期的写访问权限,然后如果右边也传入了“step”,我很困惑这怎么不是对“step”的即时读取访问”与“step”的长期写作重叠。还是仅当您为两个 inout 参数传递同一实例而不是一个 inout 和一个常规参数时才会出现问题?
写的时候
x +=5
等于
x = x + 5
首先对变量 x 进行读取操作,然后将值添加到 5,最后对结果进行写入操作,所有这些都是通过寄存器同步发生的,而不是同时发生
我知道你已经明白了,但要为未来的读者澄清一下;在您的评论中,您说:
... 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
也不会),因为它构成了一系列 "instantaneous" 访问。因此,尽管我们有(使用您的短语)"code changing self by reading self first",但这不是问题,因为它们的持续时间不重叠。
只有当您处理 "long-term" 访问时,问题才会显现出来。 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
虽然您已经可以长期访问它。