值和引用类型 Value and Reference Types
在Swift中,有两种数据类型. 一是"值类型"(value type), 它是每一个实例都保存有各自的数据,通常定义为struct, enum或tuple. 二是"引用类型"(reference types),它是多实例共享一份数据,这种类型通常定义为class. 在本文中,我们将展示值类型和引用类型各自的优点以及如何在二者之间选择.
它们有什么区别?
最基本的区别是 "值类型"通过拷贝(在赋值,初始化,参数传递中)会创建一个拥有独立数据的实例, 例如:
// 值类型实例 func value_type_example(){ struct S{var data:Int = -1} var a = S() var b = a // a 拷贝到 b a.data = 42 // 修改a, b不变 println("\(a.data), \(b.data)") // 打印 42, -1 }
引用类型拷贝, 从另一方面来说, 是隐式的创建了一个共享实例. 在拷贝后, 两个变量实际引用到了同一个实例, 也就是说修改第二个变量的值同样会影响原始值, 例如:
func reference_type_example(){ class C{var data: Int = -1} var x = C() var y = x // x 拷贝到 y x.data = 42 // 修改x, 影响到y println("\(x.data), \(y.data)") // 打印 42, 42 }
可变性在安全的作用(The Role of Mutation in Safety)
一个选择值类型(而不是引用类型)的主要原因是使代码更简单. 如果你总是得到一个唯一, 拷贝的实例, 你就可以确信其它代码不会改变它. 这在多线程环境中非常有效(尤其是不同线程可能修改你的值, 这样容易产生非常难以调试的错误).
因为两者的区别在于数据改变, 因此在实例没有可写数据时, 值类型和引用类型会有相同的场景, 它们的作用是一致的.
你可能想到用值类型,然后,在另一个方面可能是一个不可变类. 这种情况下, 使用NSObject对象可以简化问题, 它同时有值类型的优点. 现在,你可以在Swift中写一个不可变类(只有不可变属性,避免暴露能修改状态的APIs). 实际上, 许多能用Cocoa类(比如NSURL)都设计为不可修改类. 然而, Swift当前并没有提供任何语言机制强迫class不可变(比如子类). 只有struct和enum才强制不可变.
如何选择呢?
如果你需要一个新类型, 你会选哪一种? 当你与Cocoa一起工作时, 许多APIs需要从NSObject子类化, 因此你只有用class. 在其它情况下, 这里有一些指导方针:
使用值类型:
- 通过==比较实例数据
- 通过拷贝拥有独立状态
- 数据在多线程中使用
使用引用类型:
- 通过===比较实例数据
- 共享可修改状态
在Swift中, Array,String和Dictionary都是值类型. 它们的行为非常象C中的简单int值, 每个实例都有独立的数据. 你不需要做任何其它事情(比如拷贝)来阻止其它代码来修改数据. 重要的是, 你可以安全的在线程之间传递变量不需要处理同步问题. 在改善安全性的思路下, 这样的模型将帮助你编写可预知的代码.