swift值类型何时进行复制

swift值类型何时进行复制,swift,Swift,在Swift中,当你传递一个值类型时,比如说一个数组给一个函数。将为函数创建一个要使用的数组副本 然而,网站上的文件也指出: 上面的描述是指字符串、数组和数组的“复制” 字典。您在代码中看到的行为将始终是 复制发生了。但是,Swift只执行实际的备份 绝对有必要这样做的场景。斯威夫特管理一切 重视复制以确保最佳性能,您不应避免 分配以尝试抢占此优化 那么,这是否意味着复制实际上只在修改传递的值类型时进行 有没有办法证明这实际上是潜在的行为 为什么这很重要?如果我创建了一个大的不可变数组,并希望将

在Swift中,当你传递一个值类型时,比如说一个数组给一个函数。将为函数创建一个要使用的数组副本

然而,网站上的文件也指出:

上面的描述是指字符串、数组和数组的“复制” 字典。您在代码中看到的行为将始终是 复制发生了。但是,Swift只执行实际的备份 绝对有必要这样做的场景。斯威夫特管理一切 重视复制以确保最佳性能,您不应避免 分配以尝试抢占此优化

那么,这是否意味着复制实际上只在修改传递的值类型时进行

有没有办法证明这实际上是潜在的行为

为什么这很重要?如果我创建了一个大的不可变数组,并希望将它从一个函数传递到另一个函数,那么我当然不想继续复制它。在这种情况下,我应该只使用nsarray,还是只要我不尝试操纵传入的数组,Swift数组就可以正常工作


现在,只要我没有使用var或inout显式地使函数中的变量可编辑,那么函数就不能修改数组。那它还能复制吗?假定另一个线程可以在其他地方修改原始数组(仅当它是可变的时),在调用函数时制作副本是必要的(但仅当传入的数组是可变的时)。因此,如果原始数组是不可变的,并且函数没有使用var或inout,那么Swift创建副本就没有意义了。对吗?那么苹果上面的这个短语是什么意思呢

我不知道Swift中的每种值类型是否都是相同的,但是对于
Array
s,我非常确定它是写时的副本,所以除非修改它,否则它不会复制它,正如你所说的,如果你将它作为常量传递,你无论如何都不会冒这种风险

p、 在Swift 1.2中,您也可以使用新的API在您自己的值类型上实现写时拷贝

TL;博士:

那么,这是否意味着复制实际上只在修改传递的值类型时进行

对!

有没有办法证明这实际上是潜在的行为

请参阅“写时拷贝优化”一节中的第一个示例

在这种情况下,我应该使用NSArrray还是Swift阵列可以正常工作 只要我不试图操纵传入的数组

如果将数组作为
inout
传递,则将具有一个传递引用语义, 因此,显然避免了不必要的拷贝。 如果将数组作为普通参数传递, 然后,写时拷贝优化将启动,您不应该注意到任何性能下降 同时仍然受益于使用
NSArray
可以获得的更多类型安全性

现在只要我没有显式地使函数中的变量可编辑 通过使用var或inout,该函数无论如何都不能修改数组。 那它还能复制吗

你会得到一份抽象意义上的“副本”。 实际上,由于采用了写时拷贝机制,底层存储将被共享, 因此,避免不必要的副本

如果原始数组是不可变的,且函数未使用var或inout, Swift创建副本没有意义。对吧?

确切地说,这就是写时拷贝机制

那么苹果上面的这个短语是什么意思呢

本质上,苹果意味着你不必担心复制价值类型的“成本”, 斯威夫特在幕后为您优化了它

相反,您应该只考虑值类型的语义, 也就是说,一旦分配或使用它们作为参数,就可以获得一份副本。 Swift的编译器实际生成的是Swift的编译器业务

值类型语义 Swift确实将数组视为值类型(与引用类型相反), 以及结构、枚举和大多数其他内置类型 (即,属于标准库而非基础的部分)。 在内存级别,这些类型实际上是不可变的普通旧数据对象(POD), 这可以实现有趣的优化。 实际上,它们通常是在堆栈而不是堆上分配的[1], (). 这使得CPU能够非常高效地管理它们, 并在函数退出时自动释放内存[2], 不需要任何垃圾收集策略

无论何时作为函数赋值或传递,都会复制值。 这种语义学有很多优点, 例如避免创建意外的别名, 同时也使编译器更容易保证值的生存期 存储在另一个对象中或由闭包捕获。 我们可以想一想,管理好的旧C指针来理解原因有多难

有人可能会认为这是一个考虑不周的战略, 因为每次分配变量或调用函数时都需要复制。 但这可能违反直觉, 如果不比传递引用便宜的话,复制小型类型通常是相当便宜的。 毕竟,指针通常与整数大小相同

但是,对于大型集合(即数组、集合和字典),关注是合理的, 以及在较小程度上非常大的结构[3]。 但是编译器有一个技巧来处理这些问题,即写时复制(见下文)

那变异呢 结构可以定义
变异
方法,
允许对结构的字段进行变异。
这与值类型只不过是不可变的pod这一事实并不矛盾,
事实上,调用
变异
方法只是一个巨大的语法糖
将一个变量重新指定为一个全新的值,该值与以前的值相同,
除了变异的字段。
以下示例说明了这种语义等价性:
struct S {
  var foo: Int
  var bar: Int
  mutating func modify() {
    foo = bar
  }
}

var s1 = S(foo: 0, bar: 10)
s1.modify()

// The two lines above do the same as the two lines below:
var s2 = S(foo: 0, bar: 10)
s2 = S(foo: s2.bar, bar: s2.bar)
func f(x: inout [Int]) {
  x.append(12)
}

var a = [0]
f(x: &a)

// Prints '[0, 12]'
print(a)
func f(x: inout NSArray) {
  x = [12]
}

var a: NSArray = [0]
f(x: &a)

// Prints '(12)'
print(a)
let array1 = [1, 2, 3]
var array2 = array1

// Will print the same address twice.
array1.withUnsafeBytes { print($0.baseAddress!) }
array2.withUnsafeBytes { print($0.baseAddress!) }

array2[0] = 1

// Will print a different address.
array2.withUnsafeBytes { print($0.baseAddress!) }
var array1 = [[1, 2], [3, 4]]
var array2 = array1

// Will print the same address twice.
array1[1].withUnsafeBytes { print($0.baseAddress!) }
array2[1].withUnsafeBytes { print($0.baseAddress!) }

array2[0] = []

// Will print the same address as before.
array2[1].withUnsafeBytes { print($0.baseAddress!) }
final class Wrapped<T> {
  init(value: T) { self.value = value }
  var value: T
}

struct CopyOnWrite<T> {
  init(value: T) { self.wrapped = Wrapped(value: value) }
  var wrapped: Wrapped<T>
  var value: T {
    get { return wrapped.value }
    set {
      if isKnownUniquelyReferenced(&wrapped) {
        wrapped.value = newValue
      } else {
        wrapped = Wrapped(value: newValue)
      }
    }
  }
}

var a = CopyOnWrite(value: SomeLargeObject())

// This line doesn't copy anything.
var b = a