Logo
Section 1 / 2

Swift 结构体

深入了解 Swift 结构体,并详细探讨值类型和引用类型的概念。

检查点

Swift 结构体

深入了解 Swift 结构体,并详细探讨值类型和引用类型的概念。

在前面的课程中,我们已经学习了 Swift 中的类。本节课将介绍结构体(struct)的使用。虽然从表面上看,结构体和类非常相似,但在选择使用哪种类型时,需要理解一些重要的区别。本节课将讲解如何声明和使用结构体,探讨结构体与类之间的差异,并引入值类型和引用类型的概念。

Swift 结构体概览

与类一样,结构体是面向对象编程的基础,提供了一种将数据和功能封装为可重用实例的方式。结构体的声明方式与类类似,唯一的不同是使用 struct 关键字代替 class 关键字。例如,下面的代码定义了一个简单的结构体,包含一个 String 类型的变量、一个初始化器和一个方法:

1struct SampleStruct { 2 3 var name: String 4 5 init(name: String) { 6 self.name = name 7 } 8 9 func buildHelloMsg() -> String { 10 return "Hello " + name 11 } 12}

将上述结构体声明与对应的类声明进行比较:

1class SampleClass { 2 3 var name: String 4 5 init(name: String) { 6 self.name = name 7 } 8 9 func buildHelloMsg() -> String { 10 return "Hello " + name 11 } 12}

除了使用 struct 关键字替代 class 外,两者的声明完全相同。两种类型的实例也使用相同的语法创建:

1let myStruct = SampleStruct(name: "Mark") 2let myClass = SampleClass(name: "Mark")

与类类似,结构体也可以被扩展,能够遵循协议,并且可以包含初始化器。

由于类和结构体具有高度的相似性,因此理解它们之间的区别至关重要。在深入探讨最重要的区别之前,首先需要了解 值类型 和 引用类型 的概念。

值类型 vs 引用类型

尽管结构体和类在外观上相似,但当结构体或类的实例被复制或作为参数传递给方法或函数时,它们的行为存在显著差异。这是因为结构体实例是值类型,而类实例是引用类型。

当一个结构体实例被复制或传递给方法时,会创建该实例的一个完整副本,包括其中包含的所有数据。这意味着副本拥有自己独立的数据版本,与原始结构体实例无关。换句话说,在运行的应用程序中,可以存在多个结构体实例的副本,每个副本都持有其自身独立的数据副本。对其中一个副本的修改不会影响其他副本。

相比之下,当一个类实例被复制或作为参数传递时,仅复制或传递的是指向该类实例在内存中位置的引用。通过这些引用对实例所做的任何更改,都会作用于同一个实例。换句话说,只有一个类实例,但可能有多个引用指向它。通过任意一个引用对实例数据进行修改,所有其他引用看到的数据也会随之改变。

为了直观展示值类型和引用类型的行为,考虑以下代码:

1struct SampleStruct { 2 3 var name: String 4 5 init(name: String) { 6 self.name = name 7 } 8 9 func buildHelloMsg() -> String { 10 return "Hello " + name 11 } 12} 13 14let myStruct1 = SampleStruct(name: "Mark") 15print(myStruct1.name)

执行这段代码后,输出结果为 “Mark”。现在修改代码,创建 myStruct1 的一个副本,修改其 name 属性,并分别打印两个实例的名称:

1struct SampleStruct { 2 3 var name: String 4 5 init(name: String) { 6 self.name = name 7 } 8 9 func buildHelloMsg() -> String { 10 return "Hello " + name 11 } 12} 13 14let myStruct1 = SampleStruct(name: "Mark") 15var myStruct2 = myStruct1 16myStruct2.name = "David" 17 18print(myStruct1.name) 19print(myStruct2.name)

执行后,输出结果为:

1Mark 2David

显然,名称的更改只影响了 myStruct2,因为它是 myStruct1 的一个实际副本,拥有自己的数据副本,如图所示:

...
值类型

再来看类的例子:

1class SampleClass { 2 3 var name: String 4 5 init(name: String) { 6 self.name = name 7 } 8 9 func buildHelloMsg() -> String { 10 return "Hello " + name 11 } 12} 13 14let myClass1 = SampleClass(name: "Mark") 15var myClass2 = myClass1 16myClass2.name = "David" 17 18print(myClass1.name) 19print(myClass2.name)

执行后,输出结果为:

1David 2David

在这种情况下,name 属性的更改同时反映在 myClass1 和 myClass2 上,因为两者都是指向同一个类实例的引用,如图所示:

...
引用类型

除了上述值类型和引用类型的区别外,结构体不支持继承和子类化,即一个结构体无法从另一个结构体继承。此外,结构体不能包含析构器(deinit)方法。最后,虽然可以在运行时识别类实例的类型,但无法对结构体做同样的操作。

注意: 通常情况下,除非需要类特有的功能,否则应优先使用结构体。

总结

Swift 的结构体和类都提供了创建实例的机制,用于定义属性、存储值并定义方法。尽管两者看起来很相似,但在实例被复制或传递给方法时,行为上有显著差异。类属于引用类型,而结构体属于值类型。当复制或传递结构体实例时,会创建一个全新的实例,包含独立的数据副本;而类实例则是通过引用传递或复制,所有引用都指向同一个实例。类独有的特性还包括支持继承、析构器以及在运行时识别类型的能力。

课程回顾

  • 当传递值类型实例作为参数时,实际传递的是该实例的副本。

  • 对某个值类型实例副本的修改,不会影响原始实例或其他副本。

  • 传递引用类型实例时,传递的是对原始实例的引用,而非副本。

  • 对任一引用所做的修改,都会反映在所有其他引用上。

  • 结构体使用 struct 关键字声明。

  • 结构体实例是值类型。

  • 类实例是引用类型。

测验

完成以下测验,以确保你理解了类与结构体之间的区别。

Quiz

单选题

Swift 类是以下哪种类型?

1 / 4

Swift 面向对象编程

Swift 枚举