不确定这个inout场景是如何安全的,因为数组在返回时会失效

时间:2015-11-13 20:31:18

标签: arrays swift invalidation

下面的代码并没有崩溃,但我无法解释为什么,因为有限的"有限的"文档可用。

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

1 个答案:

答案 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中,我们修改了原始数组,将元素转换为456b的分配已更改a[2]如预期的那样。

在案例2中,我们将[4, 5, 6]分配给a,但未将原始值更改为456,但而是将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。亦是   让我们永远不变!

设置a1b被修改的事实表明a1指向原始数组的内存。斯威夫特显然没有考虑将b设置为修改a1。也许是因为在使用a调用foo时,已确保&a[2]是可变的。