下面的代码并没有崩溃,但我无法解释为什么,因为有限的"有限的"文档可用。
func foo(inout a: [Int], inout b: Int) {
a = []
b = 99
}
var arr = [1,2,3]
// confusion: where does the "out" of b go to?
// storage for a was already cleared / invalidated
foo(&arr, b: &arr[2])
print(arr) // arr is empty
答案 0 :(得分:2)
我相信这就是正在发生的事情。分配
时a = []
您将a
指向新数组。原始数组仍然存在于内存中,当您执行以下操作时:
b = 99
您正在修改原始数组,而不是a
引用的新数组。
你有什么证据证明是这种情况?
考虑对您的实验进行此修改:
案例1:
func foo(inout a: [Int], inout b: Int) {
a[0] = 4
a[1] = 5
a[2] = 6
b = 99
}
var arr = [1,2,3]
foo(&arr, b: &arr[2])
print(arr) // prints "[4, 5, 99]"
现在考虑一下:
案例2:
func foo(inout a: [Int], inout b: Int) {
a = [4, 5, 6]
b = 99
}
var arr = [1,2,3]
foo(&arr, b: &arr[2])
print(arr) // prints "[4, 5, 6]"
显然,修改a
的各个元素与将数组分配给a
不同。
在案例1中,我们修改了原始数组,将元素转换为4
,5
和6
,b
的分配已更改a[2]
如预期的那样。
在案例2中,我们将[4, 5, 6]
分配给a
,但未将原始值更改为4
,5
和6
,但而是将a
指向新数组。在这种情况下,b
的分配不会更改a[2]
,因为a
现在指向内存中不同位置的新数组。
案例3:
func foo(inout a: [Int], inout b: Int) {
let a1 = a
a = [4, 5, 6]
b = 99
print(a) // prints "[4, 5, 6]"
print(a1) // prints "[1, 2, 99]"
}
var arr = [1,2,3]
foo(&arr, b: &arr[2])
print(arr) // prints "[4, 5, 6]"
在案例3中,我们能够在将新数组分配给a1
之前将原始数组分配给a
。这为我们提供了原始数组的名称。分配b
后,a1[2]
会被修改。
来自评论:
你的答案解释了为什么在函数内部赋值给b 作品。但是,当foo结束并将inout变量复制回来时,at 那一点我不知道如何迅速知道推迟解除分配 原始数组,直到& [2]赋值。
这可能是ARC引用计数的结果。原始数组通过引用foo
传递,引用计数递增。原始数组不会被释放,直到引用计数在foo
结束时递减。
它似乎和博士已经不允许的一样毛茸茸 - 通过了 相同的变量是inout的两倍。你的案例3也令人惊讶。不该'吨 let a1 =一行执行struct / value语义并复制一个快照 那么阵列呢?
是。我同意案例3令人惊讶,但它确实揭示了一些正在发生的事情。通常,当您将一个数组分配给一个新变量时,Swift不会立即复制。相反,它只是将第二个数组指向第一个数组,并且引用计数递增。这样做是为了提高效率。只有在修改其中一个数组时才需要复制。在这种情况下,当a
被修改时,a1
会将数组的原始副本保留在内存中。
这真令人困惑;我不明白为什么a1不会得到1,2,3。亦是 让我们永远不变!
设置a1
时b
被修改的事实表明a1
指向原始数组的内存。斯威夫特显然没有考虑将b
设置为修改a1
。也许是因为在使用a
调用foo
时,已确保&a[2]
是可变的。