Arrays 是否存在Swift数组分配不一致的原因(既不是引用也不是深度副本)?
我正在阅读文档,我不断地对语言的一些设计决策摇头。但真正让我困惑的是数组是如何处理的 我冲向操场,试了试。你也可以试试。第一个例子是:Arrays 是否存在Swift数组分配不一致的原因(既不是引用也不是深度副本)?,arrays,swift,Arrays,Swift,我正在阅读文档,我不断地对语言的一些设计决策摇头。但真正让我困惑的是数组是如何处理的 我冲向操场,试了试。你也可以试试。第一个例子是: var a = [1, 2, 3] var b = a a[1] = 42 a b 这里的a和b都是[1,42,3],我可以接受。数组被引用-好 现在看这个例子: var c = [1, 2, 3] var d = c c.append(42) c d c是[1,2,3,42]但是d是[1,2,3]。也就是说,d在上一个示例中看到了更改,但在这个示例中没有看
var a = [1, 2, 3]
var b = a
a[1] = 42
a
b
这里的a
和b
都是[1,42,3]
,我可以接受。数组被引用-好
现在看这个例子:
var c = [1, 2, 3]
var d = c
c.append(42)
c
d
c
是[1,2,3,42]
但是d
是[1,2,3]
。也就是说,d
在上一个示例中看到了更改,但在这个示例中没有看到。文件上说那是因为长度改变了
现在,这个怎么样:
var e = [1, 2, 3]
var f = e
e[0..2] = [4, 5]
e
f
e
是[4,5,3]
,很酷。多索引替换很好,但是f
仍然没有看到更改,即使长度没有更改
总之,如果更改1个元素,数组的公共引用会发生更改,但如果更改多个元素或附加项,则会生成一个副本
对我来说,这似乎是一个非常糟糕的设计。我这样想对吗?我不明白为什么数组应该这样做,有什么原因吗
编辑:数组已更改,现在具有值语义。更理智 请注意,数组语义和语法在Xcode beta 3版本中发生了更改,因此该问题不再适用。以下答案适用于beta 2:
这是出于性能原因。基本上,他们尽量避免复制数组(并声称具有“类似C的性能”)。引用该语言: 对于阵列,仅当执行可能修改阵列长度的操作时,才会进行复制。这包括追加、插入或删除项,或使用范围下标替换数组中的一系列项 我同意这有点让人困惑,但至少对它的工作原理有一个清晰而简单的描述
该部分还包括有关如何确保数组被唯一引用、如何强制复制数组以及如何检查两个数组是否共享存储的信息。我发现:当且仅当操作有可能更改数组的长度时,该数组将是引用数组的可变副本。在上一个示例中,
f[0..2]
使用多个索引时,该操作可能会更改其长度(可能是不允许重复),因此它会被复制
var e = [1, 2, 3]
var f = e
e[0..2] = [4, 5]
e // 4,5,3
f // 1,2,3
var e1 = [1, 2, 3]
var f1 = e1
e1[0] = 4
e1[1] = 5
e1 // - 4,5,3
f1 // - 4,5,3
:
请注意,使用下标语法设置新值时不会复制数组,因为使用下标语法设置单个值不会更改数组的长度。但是,如果将新项附加到数组,则会修改数组的长度。这将提示Swift在附加新值的位置创建数组的新副本。从今以后,a是阵列的一个独立副本
阅读本文档中阵列的分配和复制行为的整个部分。您会发现,当您替换阵列中的一系列项时,阵列会为所有项获取自身的副本。使用Xcode 6 beta 3时,行为发生了变化。数组不再是引用类型,并且具有写时复制机制,这意味着只要您从一个或另一个变量更改数组的内容,数组将被复制,并且仅更改一个副本
旧答案: 正如其他人所指出的,Swift尽可能避免复制数组,包括一次复制 如果要确保数组变量(!)是唯一的,即不与其他变量共享,可以调用
unshare
方法。这将复制阵列,除非它已经只有一个引用。当然,您也可以调用copy
方法,该方法将始终创建副本,但最好取消共享以确保没有其他变量保留在同一数组中
var a = [1, 2, 3]
var b = a
b.unshare()
a[1] = 42
a // [1, 42, 3]
b // [1, 2, 3]
对我来说,如果先用变量替换常量,这更有意义:
a[i] = 42 // (1)
e[i..j] = [4, 5] // (2)
第一行永远不需要更改a
的大小。特别是,它不需要进行任何内存分配。无论i
的值是多少,这都是一个轻量级操作。如果你想象引擎盖下的a
是一个指针,它可以是一个常量指针
第二行可能要复杂得多。根据i
和j
的值,您可能需要执行内存管理。如果设想e
是一个指向数组内容的指针,那么就不能再假设它是一个常量指针;您可能需要分配一个新的内存块,将数据从旧内存块复制到新内存块,并更改指针
语言设计者似乎试图尽可能地保持(1)的轻量级。由于(2)可能涉及复制,他们求助于解决方案,它总是像你复制一样
这很复杂,但我很高兴他们没有让它变得更复杂,例如“如果(2)中的I和j是编译时常数,编译器可以推断e的大小不会改变,那么我们就不会复制”这样的特殊情况
最后,基于我对Swift语言设计原则的理解,我认为一般规则如下:
- 默认情况下,在任何地方都使用常量(
),这样就不会有任何重大意外让
- 只有在绝对必要的情况下才使用变量(
),并且在这些情况下要小心变化,因为会出现意外情况[这里:在某些但不是所有情况下数组的奇怪隐式副本]var
- 行为
var a = [1, 2, 3]
var b = a.copy()
a[1] = 42
var a = [1, 2, 3]
var b = a
a[1] = 42
a
b