Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Arrays 是否存在Swift数组分配不一致的原因(既不是引用也不是深度副本)?_Arrays_Swift - Fatal编程技术网

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