一、类和结构体的对比
(一)、类和结构体的相同点
- 定义存储值的属性;
- 定义提供功能的方法;
- 定义下标以使用下标语法提供对其值的访问;
- 定义初始化器来设置其初始状态;
- 通过扩展增加新功能;
- 遵守协议来提供某种标准功能;
(二)、类和结构体的不同点
类具有结构体不具备的其他功能:
- 继承使一个类可以继承另一个类的特性;
- 类型转换使你可以在运行时检查和解释类实例的类型;
- 析构器使类的实例可以释放它已分配的任何资源;
- 引用计数器允许对一个类实例的多个引用;
(三)、如何选择类和结构体
在应用程序中存储数据和建模行为时,结构和类是很好的选择,但是它们的相似性使得很难正确选择使用哪种数据类型。
考虑以下建议,可以帮助你在向应用程序中添加新数据类型时选择合适的数据类型。
- 默认情况下使用结构;
- 在需要Objective-C互操作性时使用类;
- 当您需要控制正在建模的数据的标识时,请使用类;
- 使用结构体和协议通过共享实现来采用行为;
参考资料: - 如何选择类和结构体
二、定义语法
类和结构体的定义语法:
struct SomeStructure {
// structure definition goes here
}
class SomeClass {
// class definition goes here
}
定义类和结构体的示例:
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
二、结构体的全能初始化器
所有结构都有一个自动生成的成员初始化器,可以使用它初始化新结构实例的所有成员属性。
let vga = Resolution(width: 640, height: 480)
与结构不同,类实例不接收默认的全能初始化器。
三、结构体和枚举是值类型
值类型是一种类型,其值在被赋值给变量或常量时被复制,或者在传递给函数时被复制。
Swift中的所有的基本类型:整数、浮点数、布尔、字符串、 数组 和 字典都是值类型,并且在底层的实现是结构体。同时Swift中所有的枚举和结构体也是值类型,这意味着我们创建的任何结构和枚举实例,以及作为它们属性的任何值类型,在代码中传递时始终会被复制。
注意:
标准库中定义的集合(如数组,字典和字符串)使用了优化来降低复制时的性能消耗。 这些集合不是立即复制,而是共享内存,其中元素存储在原始实例和任何副本之间。 如果修改了这个集合的其中一个副本,则在修改之前复制元素。 你在代码中看到的行为总是好像立即发生了复制。
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048
print("cinema is now \(cinema.width) pixels wide")
// Prints "cinema is now 2048 pixels wide"
print("hd is still \(hd.width) pixels wide")
// Prints "hd is still 1920 pixels wide"
上述示例中,首先声明了一个常量hd
,将其设置为一个Resolution
类型的结构体实例。然后又声明了一个变量cinema
,将其设置为hd
当前的值。由于Resolution
是一个结构体类型,因此会生成该实例的副本,并将新的副本分配给cinema
。尽管hd
和cinema
现在有相同的width
和height
,但是在内存中它们是两个完全不同的实例。因此当修改cinema
中的width
属性时,对hd
中的width
属性并没有影响。它们在内存中的分配如下图所示:
同理,枚举也是相同的。
enum CompassPoint {
case north, south, east, west
mutating func turnNorth() {
self = .north
}
}
var currentDirection = CompassPoint.west
let rememberedDirection = currentDirection
currentDirection.turnNorth()
print("The current direction is \(currentDirection)")
print("The remembered direction is \(rememberedDirection)")
// Prints "The current direction is north"
// Prints "The remembered direction is west"
四、类是引用类型
与值类型不同,引用类型在分配给一个变量或常量,或者传递给函数时不会被拷贝。 不是使用拷贝的对象,而是使用对同一个现有实例对象的引用。
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0
let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0
print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// Prints "The frameRate property of tenEighty is now 30.0"
上述示例中,由于类是引用类型,tenEighty
和alsoTenEighty
都引用相同的VideoMode
实例对象。实际上,它们只是同一个实例对象的两个不同名称,如下图所示:
五、相等性判断
因为类是引用类型,所以多个常量和变量在内存中可能引用的是同一个类的实例对象。 而对于结构和枚举来说情况并非如此,因为它们在分配给常量或变量或传递给函数时总是被复制。
有时可以判断两个常量或变量是否指向同一个类的相同实例对象。 为了实现这一点,Swift提供了两个身份运算符:
- 相同的 (===)
- 不完全相同(!==)
使用这些运算符可以检查两个常量或变量是否引用同一个实例对象:
if tenEighty === alsoTenEighty {
print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// Prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."
===
并不等同于 ==
,===
意味着class
类型的两个常量或变量引用完全相同的类实例对象。而==
意味着两个实例在值上被认为是相等的或等价的,对于相等的某些适当含义,由类型的设计者定义。
当自定义结构和类时,你有责任确定两个实例相等的条件。 在等价运算符中描述了定义自己的“等于”和“不等于”运算符的实现的过程。 Equivalence Operators
六、指针
如果你熟悉C,C ++或Objective-C,你可能知道这些语言都使用指针来引用内存中的地址。 Swift中的常量或变量对某个引用类型的实例的引用,类似于C中的指针,但是它不是指向内存中地址的直接指针,并且也不需要你使用星号(*)来指明正在创建一个引用。 相反,这些引用的定义与Swift中的任何其他常量或变量一样。 标准库提供了指针和缓冲区类型,如果需要直接与指针交互,可以使用这些类型- 请参阅手动内存管理。