来自documentation for inout
parameters:
输入输出参数传递如下:
- 调用该函数时,将复制参数的值。
- 在功能正文中,副本被修改。
- 当函数返回时,副本的值将分配给原始参数。
醇>
从这个描述中,我认为修改作为inout
参数传递的变量,在它作为inout
传递的范围内,是没有意义的,因为原始变量是有保证的在呼叫返回后被覆盖。作为一个人为的例子:
var x: Int = 5
({ (inoutX: inout Int) in
inoutX = 7
x = 6
})(&x)
print(x) // Expecting "7"
可以通过变异捕获来访问原始变量x
,因此仍可以将其分配给。预期的打印输出为“7”,因为这是函数调用结束时inoutX
的值。但是如果我在Swift 4 REPL中运行它,我实际上得到“6”!
该文档阐明了这种行为:
作为优化,当参数是存储在内存中物理地址的值时,在函数体内部和外部都使用相同的内存位置。
但接下来是一个非常明显不准确的声明:
优化的行为称为引用调用;它满足了拷入式拷贝模型的所有要求,同时消除了复制的开销。
显然,优化的行为不满足inout
参数声称符合的值调用结果约定。然后文档确认了这一点,但反过来说明了为什么不应该依赖 call-by-reference 行为:
使用copy-in copy-out给出的模型编写代码,而不依赖于逐个引用的优化,以便在优化或不优化的情况下它都能正常运行。
不要访问作为输入输出参数传递的值,即使原始参数在当前范围内可用。当函数返回时,对原始文件的更改将被副本的值覆盖。不要依赖于引用调用优化的实现来试图防止更改被覆盖。
我可以收集的是,inout
参数是按值调用的结果,除非它们是按引用调用的。而且,根据文档不希望您依赖于引用调用语义的程度,我只能猜测优化不是在明确定义的情况下执行的。如果是这种情况,那么我只能得出结论,访问作为inout
参数传递的变量,在inout
的范围内,是未定义的行为。
这是一个相对不幸的结论,我对如何不愿意制作文档感到困惑。为什么它会尝试将inout
参数表示为遵守特定的调用约定,当(除了setter或属性观察者)之外,这些调用约定的语义不能以定义的方式被观察到?令人困惑的是,我在这里怀疑自己的结论,所以问题就出现了:我的理解是否正确?