即使你刚刚开始编程,也可能会无意间了解到值类型。而更有可能的是你根本没有意识到自己在使用它们,但它们带有一些有趣和不可不知的属性,我将在下文中介绍它们。
什么是值类型?
你可能知道 class 是引用类型,并且可以在不同变量之间共享对象的引用。你可能还知道,可以通过引用同一个对象的多个变量来改变该对象的状态,如下所示:
class MyReferenceType {
var a: Int
init(a: Int) {
self.a = a
}
}
var R1 = MyReferenceType(a: 1)
var R2 = R1
R2.a = 42
print(R1.a, R2.a)
// 打印 "42 42"
许多不同的变量能够改变同一个对象的事实有时被称为“别名问题”,如果你不小心的话,它可能产生一些非常难以跟踪的错误。
另一方面, 值类型的行为更像我们期望的标准基础类型的行为。这意味着你期望 int
, double
和 float
所做所有的事情,值类型也都能做到。比如说在执行下面的操作时,你会期望变量 b
拥有自己可修改的数字(42)的副本,而且当它改变时变量 a
不受影响:
var a: Int = 42
var b: Int = a
b = 0
print(a, b)
// 打印 "42 0"
这种语义也扩展到了其它值类型,在 Swift 中 struct
是值类型,enum
和 tuple
也是。这就是说只要为变量被赋值给另一个变量,就会分配一个新对象。下面的代码中,修改 v2
不会影响 v1
的值:
struct MyValueType {
var a: Int
}
var V1 = MyValueType(a: 1)
var V2 = V1
V2.a = 42
print(V1)
// 打印 "MyValueType(a: 1)"
print(V2)
// 打印 "MyValueType(a: 42)"
继续深入……
正如在上一节所看到的那样,引用类型和值类型在一些非常容易理解的基本方式上有所不同。但也有一些时候,它们之间的界限变得模糊了,比如说 Array (它是个struct
)。我们将借助一个工具方法,它使我们能够比较内存地址,以便更深入地了解所发生的情况:
func address(of pointer: UnsafeRawPointer) -> String {
let length = 2 + 2 * MemoryLayout<UnsafeRawPointer>.size
let address = Int(bitPattern: pointer)
return String(format: "%0\(length)p", address)
}
var V1 = [Int]()
var V2 = V1
print(address(of: &V1) == address(of: &V2))
// 打印 "true"
V1 = [Int]()
V2 = [Int]()
print(address(of: &V1) == address(of: &V2))
// 打印 "true"
什么情况?即使我们的数组是个值类型,当它被赋值给变量 v2
时,也不会复制到新的内存地址。即使我们为每个变量创建并分配数字实例,它们也不会落在不同的内存地址上,到底怎么回事?
Swift 编译器很聪明,它会做一大通优化,以确保程序消耗尽可能少的资源。在上面的实例中,编译器注意到了这两个变量指向两个相等的数据,由于它们在执行期间没有改变,可以通过分配一个实例然后将我们的变量指向同一个实例来节省一些内存和执行时间。这种共享后资源无任何改变的行为被称作“写时复制(Copy-On-Write)”。
写时复制机制
Swift 中一大堆值类型都实现了写时复制(COW)行为,这就是说只要不发生修改,多个变量会指向相同的内存位置。如果某个变量想要改变数据,它将被分配新的内存并复制内容,然后在新内存地址上调整结构。实际上,持有 COW 值的变量不像非 COW 变量那样对该值享有“专有权”。在我们的例子调用 address
方法显示 v2
申请了它自己的内存并将 v1
的值拷贝了过去,在某种意义上第一个 MyValueType 对象将为 v1
所专有。
为了证明分配初始值的变量不一定是保留它的变量,我将贴一张 XCode Playground 的截图。看一下里面打印的内存地址,可以发现由于第一个变量想要在共享之后改变底层的值,不得不放弃初始化时分配的地址和对象,改而分配新的内存:
以上就是所有要介绍的内容,如果你想了解有关值类型的更多信息以及它可能为你的项目带来哪些优势,建议观看 WWDC 2015 上苹果的演示文稿。
原文:https://medium.com/@JimmyMAndersson/understanding-swift-value-semantics-d84d57b937a2
作者:Jimmy M Andersson
编译:码王爷