如果var似乎在Swift中深度复制数组。如果让?

时间:2016-09-15 17:42:46

标签: arrays swift deep-copy

在Swift 3.0中,下面的代码为thisArray[0]提供了不同的地址,表明该数组已被深度复制。实际情况如此,或者我在分析中遗漏了什么?如果让我们表现得一样吗?如果让它,它可能是无关紧要的,因为它是不可变的......

var thisArray: [String]? = ["One", "Two"]
withUnsafePointer(to: &thisArray![0]) {
    print("thisArray[0] has address \($0)")
}
if var thisArray = thisArray {
    withUnsafePointer(to: &thisArray[0]) {
        print("thisArray[0] has address \($0)")
    }
}

2 个答案:

答案 0 :(得分:3)

@CharlieS's answer大部分都是正确的,但会掩盖一些重要的细节......

在语义上,将值类型分配给不同的绑定(无论var变量还是let常量)总是创建副本。也就是说,您的程序代码总是可以安全地假设对值类型的一个绑定的修改永远不会影响其他绑定。

或者换一种方式:如果你是从头开始构建自己的Swift编译器/运行时/标准库版本,你可以让每个var a = ba分配新内存并复制b的所有内存内容,无论值ab是哪个值。在所有其他条件相同的情况下,您的实现将与所有Swift程序兼容。

值类型重新分配的缺点始终是副本,对于大型类型(如集合或复合类型),所有复制都会浪费时间和内存。所以......

在实践中,可以通过维护值类型的语义始终复制保证的方式实现值类型,同时提供写入时复制等性能优化。 Swift标准库集合类型(数组,字典,集合等)可以执行此操作,并且自定义值类型(包括您的类型)也可以实现写入时复制。 (有关如何的详细信息,this WWDC 2015 talk提供了一个很好的概述。)

要进行写时复制工作,实现值类型需要在内部使用引用类型(如WWDC对话中所述)。并且它必须以这样的方式来实现:值类型的语言保证 - 分配总是语义副本 - 在所有情况下都继续保持。

写时复制数组实现失败的方法之一就是允许无保护地访问其底层存储缓冲区 - 如果可以获得指向该存储的原始指针,则可以通过方式改变内容导致其他绑定(即语义副本)发生变异,违反了语言保证。

为了保留写时复制保证,标准库的集合类型确保复制可能执行无保护变异的某些操作创建副本。 (尽管如此,有时创建的副本涉及足够的引用操作,副本的内存和时间成本保持较低,直到发生实际的突变。)

你可以在Swift编译器&编辑器中看到它的一些工作原理。标准库源代码 - 从search for isUniquelyReferenced开始,跟随调用者和被调用者是ArrayBuffer等中的各种用例。

为了说明这里发生了什么,让我们尝试一下你的测试变体:

var thisArray: [String] = ["One", "Two"]
withUnsafePointer(to: &thisArray[0]) {
    print("thisArray[0] has address \($0)")
}
var thatArray = thisArray // comment/uncomment here
withUnsafePointer(to: &thisArray[0]) {
    print("thisArray[0] has address \($0)")
}

当您注释掉作业thatArray = thisArray时,两个地址都是相同的。但是,thisArray不再被唯一引用后,访问即使是原始数组的底层缓冲区也需要一个副本(或至少一些内部间接)。

答案 1 :(得分:2)

相关:https://developer.apple.com/swift/blog/?id=10

  

在Swift中,Array,String和Dictionary都是值类型。

因此,如果您通过PROC MEANSfunction theme_select_as_links($vars) {分配现有的值类型,则会发生副本。如果您通过function myawesometheme_select_as_links($vars)var分配现有的引用类型(例如类),那么您将分配引用。