美文网首页
理解 Swift 的值语义

理解 Swift 的值语义

作者: 全站工程师 | 来源:发表于2019-05-01 21:38 被阅读0次
栈中两个值类型的概念草图

即使你刚刚开始编程,也可能会无意间了解到值类型。而更有可能的是你根本没有意识到自己在使用它们,但它们带有一些有趣和不可不知的属性,我将在下文中介绍它们。

什么是值类型?

你可能知道 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"

许多不同的变量能够改变同一个对象的事实有时被称为“别名问题”,如果你不小心的话,它可能产生一些非常难以跟踪的错误。
另一方面, 值类型的行为更像我们期望的标准基础类型的行为。这意味着你期望 intdoublefloat 所做所有的事情,值类型也都能做到。比如说在执行下面的操作时,你会期望变量 b 拥有自己可修改的数字(42)的副本,而且当它改变时变量 a 不受影响:

var a: Int = 42
var b: Int = a
b = 0
print(a, b)
// 打印 "42 0"

这种语义也扩展到了其它值类型,在 Swift 中 struct 是值类型,enumtuple 也是。这就是说只要为变量被赋值给另一个变量,就会分配一个新对象。下面的代码中,修改 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
编译:码王爷

相关文章

  • 理解 Swift 的值语义

    即使你刚刚开始编程,也可能会无意间了解到值类型。而更有可能的是你根本没有意识到自己在使用它们,但它们带有一些有趣和...

  • 理解Array和NSArray的差异

    按值语义实现的Array 在Swift中,Array是按照值语义实现的,当我们复制一个Array对象时,会拷贝整个...

  • Swift - 结构体优化- 写时复制

    我们知道Swift中推荐使用具有值语义的结构体,而不是具有可变性的引用语义. Swift标准库中Array结构体是...

  • Swift中值类型和引用类型

    值类型 值类型,即每个实例保持一份数据拷贝。Swift 中,值类型的赋值为深拷贝(Deep Copy),值语义(V...

  • Swift 值类型和引用类型

    一、值语义Swift的基本类型(Array,Dictionary, Int, String等)都是用结构体实现的,...

  • 利用Swift中的值语义

    Swift整体设计的一个非常有趣的方面是它围绕价值类型概念的集中性。大多数标准库的核心类型(例如String,Ar...

  • 数组

    数组的值意义 Swift 数组具有值语义,它们从不会在不同的地方被共享使用(没有 共用的引用)不共享内存类型推断 ...

  • 360面经总结

    一面 html html语义化的理解 html语义化的标签,列举 css css盒模型 position的值及re...

  • C++11之move语义

    要理解c++11的move语义,就需要理解C++中的左值和右值和临时对象的概念。 左值与右值和临时对象的简单介绍:...

  • swift 中数组是具有值语义的

    例子1:如下边的例子,当 testX赋值给testY的时候,testX的值是不会发生改变的。在赋值的时候会自动把整...

网友评论

      本文标题:理解 Swift 的值语义

      本文链接:https://www.haomeiwen.com/subject/brawnqtx.html