【iOS 印象】Swift 中值类型与引用类型指北

1,312 阅读3分钟

值类型(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 = a
b.name = "Fluffy"

print(a.name)   // Whiskers
print(b.name)   // Fluffy

// 引用类型
class CatClass {
    init(name: String) {
        self.name = name
    }
    
    var name: String
}

let x = CatClass(name: "Whiskers")
let y = x
y.name = "Fluffy"

print(x.name)   // Fluffy
print(y.name)   // Fluffy

可变性(Mutability)概念的不同

  • 区分变量(variable)可变性与实例(instance)可变性
  • 由于值类型,变量与实例在逻辑上是统一体,因此值类型的变量可变性,即其实例的可变性
  • 引用类型,变量与其所指向的实例是分离的,因此变量的可变性,仅仅代表其始终指向同一实例,不代表其实例的可变性,其实例中的具体数据(属性)等仍保持其自身的可变性。

值类型的特性

  • ​基于属性的可等性(Attribute-based equality)
  • 没有身份标识与生命周期(Lack of Identity or lifecycle)
  • 可替代性(Substitutability)
  • 以上三个特性总结下来就是:5 美元就是 5 美元,到哪儿都是 5 美元

值类型的优点

  • ​高效:引用类型在堆上分配,相比栈上分配开支更大。Swift 中,值类型基于其特性实现了 copy on write,即当可变值类型真正发生改变那一刻时才会为其分配内存空间。
  • 无副作用(可预测):不同于引用类型的实例可被多个变量引用并操作,值类型的特性可以有效避免代码的副作用。
  • 线程安全
  • 没有内存泄露的问题
  • 易测试

值类型与引用类型在应用开发中如何设计使用

  1. ​引用类型用于构建有身份标识(identity)的模型实体(model entity)
    • 若该情况下采用值类型,则易造成同一身份标识的数据(属性)不一致问题
  2. 值类型用于封装状态,暴露行为,表述业务逻辑
  3. 使用环境非常重要
  4. 基于属性的可等性的测试
  5. 值类型与引用类型的组合使用
    • ​将对象构造在值的基础上

总结

Swift 中提供了强大而高效的值类型,让我们的一部分代码能够更高效、无副作用,并且线程安全。在理解了值类型与引用类型的区别的前提下,组合这两种数据类型,更好更高效地达成应用目标。