Swift基础

本文介绍Swift语言的基础内容,主要包括数据类型、流程控制、集合类型、函数和闭包等。

数据类型

基本数据类型

Swift包含了C和Objective-C上所有的基础数据类型:

  • Int:整数,无符号类型使用UInt。在32位平台上,Int与Int32长度相同;在64位平台上,Int与Int64长度相同
  • Float:32位浮点数
  • Double:64位浮点数
  • Bool:布尔值
  • Character:字符
  • String:字符串,是字符的集合

字符串是我们平常开发中频繁使用的数据类型,Swift也给字符串封装了相当多的功能。常见的比如字符串插值:

1
2
let a = "gcfeng"
let s = "hello \(a)" // 输出:hello gcfeng
也可以通过for-in循环来遍历字符串的每一个字符:
1
2
3
for c in s {
print(c)
}

常量与变量

如果熟悉TypeScript,会发现两者声明变量的方式很像。不同的是,Swift中使用let来定义常量,使用var来定义变量。

1
2
3
4
5
6
7
8
// 使用let来声明常量,使用var来声明变量
let s: String = "hello"

// Swift支持类型推断,这里推断出v是整型。类型推断可以在声明变量时省略类型声明
var v = 10

// Swift是类型安全的,v是整型,这里将字符串赋值给整型变量会报错
v = "error"

元组

除了上面基础的数据类型,Swift还增加了元组。元组将多个值组合成了一个复合值。元组内的值可以是任意类型,比如(200, "OK")是一个被描述为(Int, String)类型的元组。元组在函数有多个返回值时会非常有用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 声明一个元组
let httpCode = (200, "OK")

// 解构赋值,与ES类似
let (status, code) = httpCode

// 可以通过下标来访问元组的值
print(httpCode.0)
print(httpCode.1)

// 也可以定义命名元组
let namedHttpCode = (status: 200, code: "OK")
print(namedHttpCode.status)
print(namedHttpCode.code)

可空类型

可空类型(optional)用来指定某个实例要么有值可用,要么没有值为nil。在Objective-C中,nil表示一个指向不存在对象的指针,然而在Swift中,nil是一个确定的值。

1
2
3
4
5
6
7
8
9
10
11
// 声明可空类型。这里没有赋值,默认就是nil
var str: String?
str = "hello"

// 如果我们确定str有值,可以使用!强制展开,否则会报错
let val = str!

// 使用if语句来展开,如果str为nil,不会进入到条件语句内部
if let val = str {
print(val)
}
另外一种声明可空类型的方式是在变量名的后面加上!。这种方式定义的可空类型与普通的相比只是不需要展开。通常这种方式只在类的初始化中使用。
1
2
3
4
5
6
7
var str: String!

// str为nil,直接赋值给str2会报错
let str2: String = str

// str3的类型会演变为String?
let str3 = str
可空类型的一个常见处理操作是:要么获取其值,要么使用一个默认值。比如:
1
2
3
4
5
6
7
8
9
10
let str: String?
let str2: String
if let v = str {
str2 = v
} else {
str2 = "defualt value"
}

// 可以使用下面这条语句来完成上面的功能
let str2 = str ?? "default value"

流程控制

Swift语言提供的流程控制语句大多与其他语言类似,不再赘述。这里只是列出一些值得注意的地方。首先Swift语言中的判断语句和循环语句都需要带上花括号:

1
2
3
if someValue > 1 {
print("ok")
}
for-in循环可以遍历数字范围:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 遍历1到5
for index in 1...5 {
print("\(index)")
}

// 如果我们不需要区间序列内的值,可以使用下划线来代替
for _ in 1...5 {
print("\(index)")
}

// 同时另外的区间运算符也是支持的
// 遍历1到4
for index in 1..<5 {
print("\(index)")
}

// 假设names是一个数组,这里从names的第二项开始遍历
for name in names[2...] {
print("\(name)")
}
switch语句做了增强,case子句可以匹配多种不同模式:
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
let statusCode = 400
let errorString = "The request failed."
// 注意到每一个case子句没有写break,当匹配到一个case子句时,Swift默认执行完就会跳出
switch statusCode {
case 100, 101:
errorString += " Informational, 1xx."
// case子句支持区间运算符
case 300...307:
errorString += " Redirection, 3xx."
// case子句支持值匹配,将statusCode赋值给unknowCode
case let unknownCode:
// unknownCode常量只能在该分支中使用
errorString += "\(unknownCode) is not a known error code."
}

// 这里是更加复杂的情景
switch statusCode {
case 100, 101:
errorString += " Informational, 1xx."
case 300...307:
errorString += " Redirection, 3xx."
// 使用where子句
case let unknownCode where (unknownCode >= 200 && unknownCode < 300) || unknownCode > 505:
// unknownCode常量只能在该分支中使用
errorString += "\(unknownCode) is not a known error code."
default:
errorString = "Unexpected error"
}

集合类型

Swift语言提供了数组(Array)、集合(Set)和字典(Dictionary)三种基本的集合类型。

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
// 数组
var arr = [Int]()
arr.append(4)
arr.append(3)
arr[0] = 2
arr.remove(at: 0)
for item in arr {
print(item)
}

// 集合
var set1 = Set<String>()
var set2 = Set(["Apples", "Oranges", "Pineapple"])
for food in set2 {
print(food)
}
var set3 = Set(["Apples", "Bananas", "Cereal"])
// 并集
print(set2.union(set3))
// 交集
print(set2.intersection(set3))

// 字典
var dict1: Dictionary<String, Double> = [:]
var dict2 = Dictionary<String, Double>()
var dict3: [String:Double] = [:]
var dict4 = [String:Double]()

var dict5 = ["one": 1, "two": 2]
var dictNum = dict5["one"]

for (key, value) in dict5 {
print("\(key): \(value)")
}

函数

作为现代语言,Swift的函数语法非常灵活。

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
48
49
50
51
52
53
54
55
// sayHello1函数定义了一个函数参数,其中to为外部参数,name为内部参数,
// to和name是同一个参数在函数内外的不同名字
func sayHello1(to name: String) {
print("Hello \(name)")
}
sayHello1(to: "gc")

// 变长参数
func sayHello2(to names: String...) {
for name in names {
print("Hello \(name)")
}
}
sayHello2(to: "gc", "nan")

// 默认参数
// 默认参数应该放在函数参数列表的末尾。如果形参有默认值,那么在调用函数时可以省略实参。
func sayHello3(to name: String, greeting: String = "Hello") {
print("\(greeting): \(name)")
}
sayHello3(to: "gc") // greeting参数使用默认值

// in-out参数
// in-out参数能让函数修改函数体以外的变量。有两个注意点:
// 1、in-out参数不能有默认值
// 2、变长参数不能标记为inout
var error = "The request failed:"
func appendError(_ code: Int, toErrorString errorString: inout String) {
if code == 400 {
errorString += " bad request."
}
}
appendError(400, toErrorString: &error) // 这里很像C语言传递了个引用,函数内部就可以修改error的值

// 嵌套函数
func areaOfTriangleWith(base: Double, height: Double) -> Double {
let num = base * height
func divide() -> Double {
return num / 2
}
return divide()
}
areaOfTriangleWith(base: 3.0, height: 5.0)

// 提前退出函数:guard
// guard语句判断某些条件没有满足时,会提前退出函数。
// 其实使用if语句也能达到同样的效果
func sayHello4(to name: String?) {
guard let n = name else {
print("No name")
return
}
print("Hello \(n)")
}
sayHello4(to: nil)

闭包

闭包是自包含的函数代码块,是一种引用类型,可以在代码中被传递和引用。闭包可以捕获和存储其所在上下文中任意常量和变量的引用。闭包表达式的一般语法为:

1
2
3
{(params) -> return type in
// body
}
Swift语言的闭包表达式鼓励在常见场景中进行语法优化,下面是逐步优化的场景:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 先定义一个数组
let numbers = [1, 3, 40, 5, 10]

// 写法1
let numbersSorted = numbers.sorted(by: {
(i: Int, j: Int) -> Bool in
return i < j
})

// 写法2:利用类型推断系统来简化代码
let numbersSorted = numbers.sorted(by: { i, j in i < j })

// 写法3:编译器的类型推断可以知道闭包接受的参数个数和类型,这意味着不需要给参数命名
// 使用$0引用第一个参数值,$1引用第二个参数值
let numbersSorted = numbers.sorted(by: { $0 < $1 })

// 写法4:如果一个闭包是以一个函数的最后一个参数传递的,那么它就可以在函数的圆括号以外内联。
// 这种方式也叫做尾随闭包优化。
let numbersSorted = numbers.sorted { $0 < $1 }