Swift之类和结构体
在Swift中,类和结构体既相似又各有不同。
基本知识点:基本语法、类型方法、mutating方法、类和结构体异同点、类和结构体的选择。
基本语法
结构体和类都可以添加属性和方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 结构体
struct Resolution {
var width = 0
var height = 0
func PrintDesc(){
print("this is a structure instance method")
}
}
// 类
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
func PrintVideoDesc(){
print("this is a VideoMode instaince method")
}
}点语法访问或赋值
1
2let someResolution = Resolution()
let someVideoMode = VideoMode()Swift允许直接设置结构体属性的子属性
1
2
3let width = someResolution.width
let width2 = someVideoMode.resolution.width
someVideoMode.resolution.width = 1280结构体都有一个自动生成的成员逐一构造器,用于初始化结构体成员的属性
1
let vga = Resolution(width: 640, height: 1280)
结构体是值类型,类是引用类型。
值类型被赋予给一个变量、常量或者被传递给一个函数的时候,其值会被拷贝。Swift中,所有的基本类型如整型、浮点数、布尔值、字符串、数组和字典都是值类型,并且在底层都是以结构体的形式实现的。在Swift中,所有的结构体和枚举类型都是值类型。所以它们的实例,以及实例中所包含的任何值类型属性,在代码中传递的时候都会被复制。
引用类型在被赋予到一个变量、常量或者被传递到一个函数时,其值不会被拷贝。引用的是已存在的实例本身而不是其拷贝。
1
2
3
4
5
6
7
8
9
10
11
12
13//声明一个叫tenEighty的常量
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
print("the frameRate property of tenEighty is now \(tenEighty.frameRate)")
//the frameRate property of tenEighty is now 30.0注意:
tenEighty
和alsoTenEighty
被声明为常量而不是变量。然而你依然可以修改tenEighty.frameRate
和alsoTenEighty.frameRate
。因为tenEighty
和alsoTenEighty
这两个常量的值并未改变。他们并不存储这个VideoMode
实例,而仅仅是对VideoMode
实例的引用。所以改变的是被引用的VideoMode
的frameRate
属性,而不是引用VideoMode
的常量。类的继承
类不同于结构体和枚举的重要一点就是继承。继承可以重写类的实例方法和类型方法。被override的方法前需要加上
override
关键字来标记重写。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class A{
var name: String?
var voice: String?
func printDesc(){
print"this is A instance method."
}
}
class AA: A{
var language: String = "English"
// 重写父类方法
override func printDesc(){
print("this is a new instance method")
super.printDesc()//调用父类方法
}
}恒等运算符
因为类是引用类型,有可能有多个常量和变量同时引用同一个类实例。如果能够判定两个常量或者变量是否引用同一个类实例会很有帮助。运用等价于
===
和不等价于!==
这两个运算符可以检测两个常量或者变量是否引用同一个实例。1
2
3
4if tenEighty === alsoTenEighty {
print("tenEighty and alsoTenEighty refer to the same Resolution instance.")
}
//打印 "tenEighty and alsoTenEighty refer to the same Resolution instance."===
不同于==
:等价于
===
表示两个类类型的常量或者变量引用同一个类实例。等于==
表示两个实例的值相等或相同。
类型方法
结构体的类型方法用
static
关键字,类的类型方法用class
关键字1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 结构体
struct Square{
static func numberOfSides() -> Int{
return 4
}
}
let sides = Square.numberOfSides()
//类
class Rectangle{
class func numberOfSides() -> Int{
return 4
}
}
let rectangleSides = Rectangle.numberOfSides()禁止子类自己实现的类型方法用
static
关键字或final class
1
2
3
4
5
6
7static func funcName(){
//子类不能通过override class func funcName来重写此方法
}
final class func funcNameAgain(){
//子类不能重写
}关键字
static
告诉编译器不要让子类为这个方法提供自己的实现。
mutating方法
结构体中,一个实例方法要修改结构体的属性,必须用mutating
标记。
1 | struct Person{ |
类和结构体比较
共同点:
- 定义属性用于存储值
- 定义方法用于提供功能
- 定义下标操作使得可以通过下标语法来访问实例所包含的值
- 定义构造器用于生成初始值
- 通过扩展以增加默认实现的功能
- 实现协议以提供某种标准功能
与结构体相比,类的附加功能:
- 继承允许一个类继承另一个类的特征
- 类型转换允许在运行时检查和解释一个类实例的类型
- 析构器允许一个类实例释放任何其所被分配的资源
- 引用计数允许对一个类的多次引用
注意:
结构体总是通过被复制的方式在代码中传递,不使用引用计数。
类和结构体的选择
可以选择使用类和结构体来自定义数据类型。然而结构体实例总是通过值传递,类实例总是通过引用传递。
符合下列一个或多个条件时,适合构建结构体:
- 该数据结构的主要目的是用来封装少量相关简单数据值
- 有理由预计该数据结构的实例在被赋值或传递时,封装的数据将会被拷贝而不是被引用
- 该数据结构中存储的值类型属性,也应该被拷贝,而不是被引用
- 该数据结构不需要去继承另一个既有类型的属性或者行为
定义一个类,生成一个它的实例,并通过引用来管理和传递。实际中,这意味着绝大部分的自定义数据构造都应该是类,而非结构体。