Swift高级
本文介绍Swift语言的枚举、结构体、类、协议和扩展等相关知识。
枚举
通过enum
语法来定义一个枚举:
1
2
3
4
5
6enum Direction {
case north
case south
case east
case west
}1
2
3
4
5
6
7
8
9
10
11
12// 为每个case子句赋值,注意原始值不能相同
enum Direction: Int {
case north = 1
case south = 2
case east = 3
case west = 4
}
// 我们也没有必要像上面那样为每个case子句赋值,Swift支持隐形赋值
enum Direction: Int {
case north = 1, south, east, west
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 我们定义一个Person枚举,fullName和shortName都具有一个String的关联值
enum Person {
case fullName(String)
case shortName(String)
}
var p = Person.fullName("gcfeng")
// 编译器可以推断出p是Person类型,所以这里省略了Person前缀
p = .shortName("gc")
// 使用switch语句可以取出关联值
switch p {
case .fullName(let name):
print(name)
case .shortName(let name):
print(name)
}indirect
关键字来告诉编译器。因为Swift编译器需要知道每个实例占据多少空间,而递归枚举是无限循环的,无法计算所需内存空间,indirect
关键字告诉编译器将枚举的数据放到一个指针指向的地方。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30// 定义一个递归枚举
enum Expression {
case number(Int)
indirect case add(Expression, Expression)
indirect case multiply(Expression, Expression)
}
// 如果所有成员都是可递归的,我们可以将indirect提到enum前面
indirect enum Expression {
case number(Int)
case add(Expression, Expression)
case multiply(Expression, Expression)
}
// 定义一个函数来使用递归枚举
func evaluate(_ expression: Expression) -> Int {
switch expression {
case let .number(value):
return value
case let .add(left, right):
return evaluate(left) + evaluate(right)
case let .multiply(left, right):
return evaluate(left) * evaluate(right)
}
}
let a = Expression.number(1)
let b = Expression.number(2)
let sum = Expression.add(a, b)
evaluate(sum) // 输出:3
结构体和类
结构体和类都提供了面向对象编程的抽象封装能力。在Swift中,结构体的使用要简单的多,如果不涉及继承等复杂场景,推荐使用结构体。使用struct
来定义结构体,使用class
来定义类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 定义结构体
struct Resolution {
var width = 0
var height = 0
}
// 定义类
class VideoMode {
var resolution = Resolution()
var name: String?
}
// 初始化实例
var r = Resolution()
var v = VideoMode()static
来定义,类使用class
来定义。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// 定义结构体
struct Resolution {
var width = 0
var height = 0
static func printMsg() {
print("resolution")
}
}
// 定义类
class VideoMode {
var resolution = Resolution()
var name: String?
class func printMsg() {
print("videoMode")
}
}
// 使用类型本身来调用
Resolution.printMsg()
VideoMode.printMsg()VideoMode
类而言,如果不希望子类重写printMsg
方法,可以使用final class
来修饰printMsg
,或者使用static
来修饰printMsg
。
结构体中的方法默认不能修改属性值,比如上面Resolution
中的printMsg
。如果希望结构体中的方法能修改属性值,需要在方法声明中加上mutating
关键字。
1
2
3
4
5
6
7
8
9
10
11struct Person {
var age = 10
var name = "gc"
mutating func addAge() {
self.age += 1
}
}
var p = Person()
p.addAge()willSet
和didSet
观察器,当修改属性值时,会触发属性观察器,应用此特性,可以很方便的实现前端的MVVM功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21struct Person {
// 存储属性
var name = "gc"
// 存储属性,同时定义了didSet观察器
// 当age完成修改时,会触发didSet函数
var age = 20 {
didSet(oldAge) {
print("The age has changed to \(age) from \(oldAge)")
}
}
// 计算属性
var description: String {
get {
return "\(name) is \(age) years old"
}
}
}
var p = Person()
p.age = 10
print(p.description)
构造过程和析构过程
我们之前定义的结构体和类,都默认给属性赋了初始值,这是初始化的一种方式。实际上,我们可以通过构造器来给属性赋值。使用init
来定义构造器,语法如下:
1
2
3
4// 注意这里没有使用func来修饰
init(parameter: SomeType) {
// 初始化代码
}
- 一个指定构造器必须调用它直系父类的一个指定构造器
- 一个便利构造器必须调用这个类自身的另一个构造器
- 一个便利构造器最终一定会调用一个指定构造器
Swift通过便利构造器给一些属性赋默认值,从而达到简化参数传递个数的目的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Person {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
convenience init(name: String) {
self.init(name: name, age: 10)
}
}
var p = Person(name: "gc")1
2
3deinit {
// 释放额外的引用资源
}
协议
协议能让你定义类型需要满足的接口,满足某个协议的类型被称为符合这个协议。协议不仅能定义符合该协议的类型必须提供的属性和方法,自己还能作为类型使用,变量、函数参数和返回值都可以把协议作为类型。结构体、枚举、类都可以符合协议。一个类型可以符合多个协议,如果类有父类,父类的名字在前,然后再跟协议。Swift中的协议与Java语言的接口很像。Mac和iOS应用通常都把数据的展现和提供数据的源分离。利用协议,我们可以很好的做到这一点。
使用protocol
来定义一个协议:
1
2
3
4
5
6
7
8
9
10
11// 定义了一个协议DataSource
protocol DataSource {
// 符合DataSource协议的类型必须要有numberOfRows和numberOfColumns两个属性
// DataSource协议并不要求对这两个属性可写,所以在后面写上了{ get },表示只读就行
var numberOfRows: Int { get }
var numberOfColumns: Int { get }
func label(forColumn column: Int) -> String
func itemFor(row: Int, column: Int) -> String
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48struct Person {
let name: String
let age: Int
}
// 让Department符合DataSource协议
struct Department: DataSource {
let name: String
var people: [Person]()
init(name: String) {
self.name = name
}
mutating func add(_ person: Person) {
people.append(person)
}
// Mark: DataSource
// 实现协议定义的属性和方法
var numberOfRows: Int {
return people.count
}
var numberOfColumns: Int {
return 2
}
func label(forColumn column: Int) -> String {
switch column {
case 0: return "Name"
case 1: return "Age"
default: fatalError("Invalid column!")
}
}
func itemFor(row: Int, column: Int) -> String {
let person = people[row]
switch column {
case 0: return person.name
case 1: return String(person.age)
default: fatalError("Invalid column!")
}
}
}
var department = Department(name: "Engineering")
department.add(Person(name: "a", age: 20))
扩展
扩展可以给一个现有的类、结构体、枚举,还有协议添加新的功能。Swift的扩展可以:
- 添加计算属性
- 添加新初始化方法
- 使类型符合协议
- 添加新方法
- 添加嵌入类型
下面我们扩展已有的Double
类型:
1
2
3
4
5
6
7// 扩展Double类型,添加一个km属性
extension Double {
var km: Double { return self / 1000.0 }
}
let d = 100.0
print(d.km)
泛型
泛型是面向对象编程中实现多态的工具,Swift中的泛型与Java等语言的泛型非常类似。比如定义一个类的泛型:
1
2
3
4
5
6// 定义了一个Stack数据结构,Element是占位类型
// 在实例化类型的时候会特化具体类型,比如 var a = Stack<Int>()
struct Stack<Element> {
var items = [Element]()
...
}1
2
3
4// 让占位类型T符合Equatable协议
func checkIfEqual<T: Equatable>(_ first: T, _ second: T) -> Bool {
return first == second
}