10.3. 类是引用类型

与值型不同,引用类型在被赋予到一个变量、常量或者被传递到一个函数时,其值不会被拷贝。因此,使用的是已存在实例的引用,而不是其拷贝。

请看下面这个示例,其使用了之前定义的 VideoMode 类:

let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0

以上示例中,声明了一个名为 tenEighty 的常量,并让其引用一个 VideoMode 类的新实例。它的视频模式(video mode)被赋值为之前创建的 HD 分辨率(1920*1080)的一个拷贝。然后将它设置为隔行视频,名字设为 “1080i”,并将帧率设置为 25.0 帧每秒。

接下来,将 tenEighty 赋值给一个名为 alsoTenEighty 的新常量,并修改 alsoTenEighty 的帧率:

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

因为类是引用类型,所以 tenEightalsoTenEight 实际上引用的是同一个 VideoMode 实例。换句话说,它们是同一个实例的两种叫法,如下图所示:

sharedStateClass_2x

通过查看 tenEightyframeRate 属性,可以看到它正确地显示了底层的 VideoMode 实例的新帧率 30.0

print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// 打印 "The frameRate property of theEighty is now 30.0"

这个例子也显示了为何引用类型更加难以理解。如果 tenEightyalsoTenEighty 在你代码中的位置相距很远,那么就很难找到所有修改视频模式的地方。无论在哪使用 tenEighty,你都要考虑使用 alsoTenEighty 的代码,反之亦然。相反,值类型就更容易理解了,因为你的源码中与同一个值交互的代码都很近。

需要注意的是 tenEightyalsoTenEighty 被声明为常量而不是变量。然而你依然可以改变 tenEighty.frameRatealsoTenEighty.frameRate,这是因为 tenEightyalsoTenEighty 这两个常量的值并未改变。它们并不“存储”这个 VideoMode 实例,而仅仅是对 VideoMode 实例的引用。所以,改变的是底层 VideoMode 实例的 frameRate 属性,而不是指向 VideoMode 的常量引用的值。

恒等运算符

因为是引用类型,所以多个常量和变量可能在幕后同时引用同一个类实例。(对于结构体和枚举来说,这并不成立。因为它们作为值类型,在被赋予到常量、变量或者传递到函数时,其值总是会被拷贝。)

判定两个常量或者变量是否引用同一个实例有时很有用。为了达到这个目的,Swift 提供了两个恒等运算符:

  • 相同(===
  • 不相同(!==

使用这两个运算符检测两个常量或者变量是否引用了同一个实例:

if tenEighty === alsoTenEighty {
    print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// 打印 "tenEighty and alsoTenEighty refer to the same VideoMode instance."

请注意,“相同”(用三个等号表示,===)与“等于”(用两个等号表示,==)的不同。“相同”表示两个类类型(class type)的常量或者变量引用同一个类实例。“等于”表示两个实例的值“相等”或“等价”,判定时要遵照设计者定义的评判标准。

当在定义你的自定义结构体和类的时候,你有义务来决定判定两个实例“相等”的标准。在章节 高级运算符 的等价操作符中将会详细介绍实现自定义 == 和 != 运算符的流程。

指针

如果你有 C,C++ 或者 Objective-C 语言的经验,那么你也许会知道这些语言使用指针来引用内存中的地址。Swift 中引用了某个引用类型实例的常量或变量,与 C 语言中的指针类似,不过它并不直接指向某个内存地址,也不要求你使用星号(*)来表明你在创建一个引用。相反,Swift 中引用的定义方式与其它的常量或变量的一样。如果需要直接与指针交互,你可以使用标准库提供的指针和缓冲区类型 —— 参见 手动管理内存