第9章:结构体和类
在C、C++、Objective-C这些语言中,结构体都是一种简单的数据集合,方便的把一些不同或相同的数据类型组合成更大的数据类型,即使结构体与类在语言的语法支持层面相同了,但是使用者一般依然是把结构体作为一个数据集合来使用。在Swift语言中,对结构体的支持程度进一步提高,使其与类基本一致。
结构体和类在Swift中具有许多共同点:
- 定义存储值的属性
- 定义提供功能的方法
- 定义下标以使用下标语法提供对其值的访问
- 定义初始化程序以设置其初始状态
- 扩展以扩展其功能,超越默认实现
- 符合协议以提供某种标准功能
然而类依然比结构体强大,以下几方面是结构体没有的能力:
- 继承使一个类能够继承另一个类的特性。
- 类型转换使您可以在运行时检查和解释类实例的类型。
- Deinitializers允许类的实例释放它已分配的任何资源。
- 引用计数允许对类实例的多个引用。
以上所列的这些相同点或者不通点会在后续章节中进行介绍,如果目前不理解也没有问题。
作为通用的原则,在具体编程时,请优先选择枚举与结构体,因为类具有更多的功能是以增加其复杂性与牺牲性能为代价的,所以非必要的情况下请优先选择枚举或结构体
在其他语言中类的定义一般分为.h与.m或者.h与.cpp文件,但是在swift语言中类的定义都放在一个.swift文件之中。
9.1 定义类或结构体
结构和类具有类似的定义语法。你可以使用struct关键字和带有class关键字的类来引入结构,把它们的主体部分放在一对大括号中。
// 定义类与结构体的语法示例
struct SomeStructure {
// structure definition goes here
}
class SomeClass {
// class definition goes here
}
// 以下是定义类与结构体的语法实例
struct Resolution {
// 在这里通过0,编译器推断width、height是Int类型
// 编译器会根据文字常量的类型来推断变量或常量的类型
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
以上定义只是定义了Resolution、VideoMode是什么样子,相当于定了了模板,而没有生成具体的一个对象(或者叫作实例)。
// 一种最简单的创建对象的方法——类型后面添加括号
// 后续会详细简书类与结构体的初始化方法
let someResolution = Resolution()
let someVideoMode = VideoMode()
在以上实例中Resolution、VideoMode都包含一些属性,如width、name等,如果想要通过类或者结构体的实例访问其属性的话,可以通过实例名.属性名的方式访问。
// 通过.语法获取属性的值
print("The width of someVideoMode is \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is 0"
// 通过.语法为属性赋值
someVideoMode.resolution.width = 1280
print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is now 1280"
所有结构都有一个自动生成的成员初始化程序,您可以使用它初始化新结构实例的成员属性。可以通过名称将新实例的属性的初始值传递给成员初始值设定项:
let vga = Resolution(width: 640, height: 480)
与结构不同,类实例不接收默认的成员初始值设定项。
9.2 值类型与引用类型
在Swift中枚举与结构体是值类型,类是引用类型
如果有其他语言的编程经验,值类型可以简单理解为类似C语言中的Int这种类型,也就是说在参数传递、函数返回值等,会自动拷贝一份,对原始实例的修改不影响新的实例。但是引用类型则不同,类似于C语言中的指针pA、pB,两者都指向相同的对象,那么使用pA或者pB进行修改时,同时也修改了另外对象的值。
如果没有其他编程的经验,那么可以简单理解为枚举、结构体与Swift中的Int、String等类似,编译器会自动在需要的时候复制一份,各个值类型的实例之间互不影响。但是类是引用类型,多个实例之间如果指向相同的对象,则改变一起对象,其他示例会受到影响。
Swift中提供的集合类型也是值类型
Swift中提供的集合类型也是值类型
Swift中提供的集合类型也是值类型
重要的事情说三遍
关于值类型的演示示例如下:
// 创建一个Resolution对象,然后把它赋值给另外一个对象,请记住Resolution是结构体
// 也就是说Resolution是值类型,在进行各种操作时会自动复制一份,从而不会影响到其他已经存在的对象
// 此时cinema、hd两个对象指向不同的内存地址,修改一个对象的值,不会影响另外一个对象
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
// 修改cinema的width属性
cinema.width = 2048
// 检查cinema的width属性,显示它确实已更改为2048:
print("cinema is now \(cinema.width) pixels wide")
// Prints "cinema is now 2048 pixels wide"
// 但是,hd的width属性仍具有旧值1920:
print("hd is still \(hd.width) pixels wide")
// Prints "hd is still 1920 pixels wide"
以上代码的图形解释如下图
image.png
以上行为同样适用于枚举,因为枚举也是值类型。
enum CompassPoint {
case north, south, east, west
// 如果看不懂这个方法,不要紧后续章节会有讲解;
// 这个方法是一个可变方法(可以修改对象的属性),把对象的值修改为north
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"
与值类型不同,引用类型在分配给变量或常量时或者传递给函数时不会被复制
// 请注意VideoMode是类
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0
// 把tenEighty赋值给alsoTenEighty,同时修改alsoTenEighty的属性
let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0
// 通过检查发现tenEighty的frameRate属性也修改为了30.0
print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// Prints "The frameRate property of tenEighty is now 30.0"
引用类型的本质如下图所示——不通的对象指向相同的内存地址。
image.png
注意
在以上代码中tenEighty、alsoTenEighty虽然是let类型(常量类型)但是依然可以修改其所指对象的属性的值。这里的let类型是指tenEighty、alsoTenEighty所指向的对象不能修改,而不是所指向对象的属性不能修改。
类似于C++中的常量指针。
9.3 相等操作符
因为类是引用类型,所以多个常量和变量可以在后台引用同一个类的单个实例。有时可以找出两个常量或变量是否完全引用类的相同实例也是很有用的。为了实现这一点,Swift提供了两个相等运算符:
- 与(===)相同
- 与(!==)不一样
if tenEighty === alsoTenEighty {
print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// Prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."
请注意此处的===与==不是一个概念,如果两个对象===,那么一定==,反之则不成立
可以理解为===代表两个对象的内存完全相同,所以无论如何必然完全相同
==代表两个对象的逻辑意义上的相同,但由于其地址不同,所以不一定是===的