整理译自:
值类型(Value)与引用类型(Reference)
Swift 中结构体(struct)可定义属性与方法,可以指定初始化方法,实现协议,除了继承,类(class)能做的,结构体几乎都能做,那么什么时候用结构体,什么时候用类就成了个问题,换句话说,就是在 Swift 中,什么情况应该用值类型,什么情况下用引用类型?
- 值类型:结构体(Struct)、枚举(Enum)、元组(Tuple)
- 引用类型:类(Class)、函数(Function)
语义
- 值类型:变量与其所赋值分配的数据(值)逻辑统一,一般将其视为储存在栈(Stack)上,但实际上,一部分数据在 CPU 寄存器,其它一部分还是分配在堆(Heap)上。可以感性地将其理解为,值类型的数据(值)被包含在了变量中,不能够单独被操作。
- 引用类型:与值类型正好相反,变量与其分配的数据(实例)是分离的,引用类型的实例分配在堆(Heap)上,变量分配在栈上,仅持有该实例在内存中的地址,可以有多个变量指向同一个实例,该实例可独立地被其中任意指向它的变量操作。
// 值类型struct CatStruct { var name: String}let a = CatStruct(name: "Whiskers")var b = ab.name = "Fluffy"print(a.name) // Whiskersprint(b.name) // Fluffy// 引用类型class CatClass { init(name: String) { self.name = name } var name: String}let x = CatClass(name: "Whiskers")let y = xy.name = "Fluffy"print(x.name) // Fluffyprint(y.name) // Fluffy复制代码
可变性(Mutability)概念的不同
- 区分变量(variable)可变性与实例(instance)可变性
- 由于值类型,变量与实例在逻辑上是统一体,因此值类型的变量可变性,即其实例的可变性
- 引用类型,变量与其所指向的实例是分离的,因此变量的可变性,仅仅代表其始终指向同一实例,不代表其实例的可变性,其实例中的具体数据(属性)等仍保持其自身的可变性。
值类型的特性
- 基于属性的可等性(Attribute-based equality)
- 没有身份标识与生命周期(Lack of Identity or lifecycle)
- 可替代性(Substitutability)
- 以上三个特性总结下来就是:5 美元就是 5 美元,到哪儿都是 5 美元
值类型的优点
- 高效:引用类型在堆上分配,相比栈上分配开支更大。Swift 中,值类型基于其特性实现了 copy on write,即当可变值类型真正发生改变那一刻时才会为其分配内存空间。
- 无副作用(可预测):不同于引用类型的实例可被多个变量引用并操作,值类型的特性可以有效避免代码的副作用。
- 线程安全
- 没有内存泄露的问题
- 易测试
值类型与引用类型在应用开发中如何设计使用
- 引用类型用于构建有身份标识(identity)的模型实体(model entity)
-
- 若该情况下采用值类型,则易造成同一身份标识的数据(属性)不一致问题
- 值类型用于封装状态,暴露行为,表述业务逻辑
- 使用环境非常重要
- 基于属性的可等性的测试
- 值类型与引用类型的组合使用
-
- 将对象构造在值的基础上
总结
Swift 中提供了强大而高效的值类型,让我们的一部分代码能够更高效、无副作用,并且线程安全。在理解了值类型与引用类型的区别的前提下,组合这两种数据类型,更好更高效地达成应用目标。