Swift 学习笔记 1 - 基础部分

数据类型

基础数据类型

  • Int表示整型值
  • Double 和 Float 表示浮点型值
  • Bool 是布尔型值
  • String 是文本型数据

集合类型

  • 数组 Array
  • 集合 Set
  • 字典 Dictionary
  • 元组(Tuple),元组可以创建或者传递一组数据,比如作为函数的返回值时,可以用一个元组返回多个值

可选(Optional)类型

  • 用于处理值缺失的情况,可选表示“那有一个值,并且它等于x”,或者“那没有值”
1
2
// 可选类型加上一个 ? 来表示
var foo: Int? = 404

常量和变量

常量和变量必须在使用前声明,用 let 来声明常量,用 var 来声明变量。可以用任何字符作为常量和变量名,包括 Unicode 字符,也就是说变量名可以用中文。

Swift 跟 Python 一样,不需要用 ; 作为代码的结尾。

1
2
3
let foo = 10
var bar = 0
let 你好 = "你好世界"

和 Python 一样,也可以使用 ; 将多行代码写成一行

1
let cat = "🐱"; print(cat)

可以在一行中声明多个常量或者多个变量,用逗号隔开

1
var x = 0.0, y = 0.0, z = 0.0

类型标注

1
2
3
4
5
var foo: String
// 没有标注类型,但是会自动推导出类似为字符串
var bar = "foo"
// 可以在一行中定义多个同样类型的变量
var red, green, blue: Double

类型别名

1
2
// 以后 NewType 就等价于 UInt16
typealias NewType = UInt16

隐式解析可选类型

如果可以确定一个可选类型一定会有值,可以在调用的时候加上一个 !,来隐式解析可选类型,该变量可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值

1
2
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要惊叹号来获取值

打印输出

使用 print 打印内容

1
2
3
4
let foo = "Hello World"
print(foo)
// 可以直接在字符串中嵌入变量
print("The current value is \(foo)")

条件语句

if

1
2
3
4
5
6
7
8
9
10
11
// 布尔类型
let orangesAreOrange = true
let turnipsAreDelicious = false
if turnipsAreDelicious {
print("Mmm, tasty turnips!")
} else if orangesAreOrange {
print("Eww, turnips are horrible.")
} else {
print("Nothing")
}

如果在需要使用 Bool 类型的地方使用了非布尔值,会报错

1
2
3
4
let i = 1
if i {
// 这里会报错
}

Switch

匹配的 case 分支中的代码执行完毕后,程序会终止switch语句,而不会继续执行下一个 case 分支。这也就是说,不需要在 case 分支中显式地使用break语句

1
2
3
4
5
6
7
8
9
let someCharacter: Character = "z"
switch someCharacter {
case "a":
print("The first letter of the alphabet")
case "z":
print("The last letter of the alphabet")
default:
print("Some other character")
}

case 分支的模式也可以是一个值的区间

1
2
3
4
5
6
7
8
9
10
11
12
let approximateCount = 62
var naturalCount: String
switch approximateCount {
case 0:
naturalCount = "no"
case 1..<5:
naturalCount = "a few"
case 5..<12:
naturalCount = "several"
default:
naturalCount = "many"
}

Where

case 分支的模式可以使用where语句来判断额外的条件。

1
2
3
4
5
6
7
8
9
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
print("(\(x), \(y)) is just some arbitrary point")
}

复合匹配(Compound Cases)

当多个条件可以使用同一种方法来处理时,可以将这几种可能放在同一个case后面,并且用逗号隔开。当case后面的任意一种模式匹配的时候,这条分支就会被匹配。

1
2
3
4
5
6
7
8
9
10
let someCharacter: Character = "e"
switch someCharacter {
case "a", "e", "i", "o", "u":
print("\(someCharacter) is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
print("\(someCharacter) is a consonant")
default:
print("\(someCharacter) is not a vowel or a consonant")
}

循环

for 循环

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
// 1到10,步长为1
for i in (1...10) {
print(i)
}
// 1到10,不包含10,步长为1
for i in (1..<10) {
print(i)
}
// 10到1,步长为1
for i in (1...10).reversed() {
print(i)
}
// 0到10,不包含10,步长为2
for i in stride(from: 0, to: 10, by: 2) {
print(i)
}
// 0到10,包含10,步长为2
for i in stride(from: 0, through: 10, by: 2) {
print(i)
}
// 0.5到10,包含10,步长为2.5
for i in stride(from: 0.5, through: 10, by: 2.5) {
print(i)
}

while 循环

1
2
3
while condition {
statements
}

Repeat-While

1
2
3
repeat {
statements
} while condition

控制转移语句

控制转移语句改变你代码的执行顺序,通过它可以实现代码的跳转。Swift 有五种控制转移语句:

  • continue
  • break
  • fallthrough 贯穿
  • return
  • throw 抛出异常

如果需要让 Switch 的语法具有 C 风格的贯穿的特性,可以在每个需要该特性的 case 分支中使用fallthrough关键字。下面的例子使用fallthrough来创建一个数字的描述语句。

1
2
3
4
5
6
7
8
9
10
11
let integerToDescribe = 5
var description = "The number \(integerToDescribe) is"
switch integerToDescribe {
case 2, 3, 5, 7, 11, 13, 17, 19:
description += " a prime number, and also"
fallthrough
default:
description += " an integer."
}
// 输出 "The number 5 is a prime number, and also an integer."
print(description)

字符串

字符串用双引号

1
let someString = "Some string literal value"

初始化空字符串

1
2
3
var emptyString = "" // 空字符串字面量
var anotherEmptyString = String() // 初始化方法
// 两个字符串均为空并等价

可通过for-in循环来遍历字符串中的characters属性来获取每一个字符的值:

1
2
3
for character in "Dog!".characters {
print(character)
}

字符串插值 (String Interpolation) 是一种构建新字符串的方式,可以在其中包含常量、变量、字面量和表达式。插入的字符串字面量的每一项都在以反斜线为前缀的圆括号中:

1
2
3
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message is "3 times 2.5 is 7.5"

比较字符串可以用等于操作符(==)和不等于操作符(!=)

1
2
3
if foo1 == foo2 {
print("equal")
}

数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var someInts = [Int]()
someInts.append(3)
// 创建带有默认值的数组
var threeDoubles = [Double](count: 3, repeatedValue:0.0)
var anotherThreeDoubles = [Double](count: 3, repeatedValue: 2.5)
// anotherThreeDoubles 被推断为 [Double],等价于 [2.5, 2.5, 2.5]
// 通过两个数组相加创建一个数组
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles 被推断为 [Double],等价于 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
// 用字面量构造数组
var shoppingList: [String] = ["Eggs", "Milk"]

遍历数组

1
2
3
4
5
6
7
8
for item in shoppingList {
print(item)
}
// 如果同时需要每个数据项的值和索引值
for (index, value) in shoppingList.enumerate() {
print("Item \(String(index + 1)): \(value)")
}

元组

元组(tuples)把多个值组合成一个复合值,元组内的值可以是任意类型。

1
2
3
4
5
6
7
8
9
10
11
12
let score0 = (90, 95)
// 访问元组中的元素,没有名称时按照下标
print(score0.0)
// 元组也可以为里面的元素设置名称
let score1 = (chinese: 90, math: 95)
// 访问元组中的元素,按照分量的名称访问
print(score1.chinese)
let score2 = (chinese: 90, math: 100)
// 元组可以比较,按照元组中的元素,一个个比较
score1 < score2

集合

一个类型为了存储在集合中,该类型必须是可哈希化的–也就是说,该类型必须提供一个方法来计算它的哈希值。一个哈希值是 Int 类型的,相等的对象哈希值必须相同,比如a == b,因此必须a.hashValue == b.hashValue。

Swift 的所有基本类型 (比如 String, Int, Double 和 Bool) 默认都是可哈希化的,可以作为集合的值的类型或者字典的键的类型。

1
2
3
4
5
6
7
8
9
10
var letters = Set<Character>()
// letters 现在含有1个 Character 类型的值
letters.insert("a")
// letters 现在是一个空的 Set, 但是它依然是 Set<Character> 类型
letters = []
// 用数组字面量创建集合
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]

字典

1
2
3
4
5
6
7
8
9
// namesOfIntegers 是一个空的 [Int: String] 字典
var namesOfIntegers = [Int: String]()
// namesOfIntegers 现在包含一个键值对
namesOfIntegers[16] = "sixteen"
// namesOfIntegers 又成为了一个 [Int: String] 类型的空字典
namesOfIntegers = [:]
// 用字典字面量创建字典
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]

字典遍历

1
2
3
4
5
6
7
8
9
10
11
for (key, value) in airports {
print("\(key): \(value)")
}
for key in airports.keys {
print("Airport code: \(key)")
}
for value in airports.values {
print("Airport name: \(value)")
}

基本运算符

三目运算符

问题 ? 答案 1 : 答案 2

空合运算符(Nil Coalescing Operator)

空合运算符(a ?? b)将对可选类型 a 进行空判断,如果 a 包含一个值就进行解封,否则就返回一个默认值 b。表达式 a 必须是 Optional 类型。默认值 b 的类型必须要和 a 存储值的类型保持一致。

空合运算符是对以下代码的简短表达方法

1
a != nil ? a! : b

区间运算符(Range Operators)

闭区间运算符(a…b)定义一个包含从 a 到 b(包括 a 和 b)的所有值的区间。a 的值不能超过 b。 ‌ 闭区间运算符在迭代一个区间的所有值时是非常有用的,如在 for-in 循环中:

1
2
3
for index in 1...5 {
print("\(index) * 5 = \(index * 5)")
}

半开区间运算符

半开区间(a..<b)定义一个从 a 到 b 但不包括 b 的区间。 之所以称为半开区间,是因为该区间包含第一个值而不包括最后的值。

错误处理

跟 Java 类似,一个函数可以通过在声明中添加 throws 关键词来抛出错误消息

1
2
3
func canThrowAnError() throws {
// 这个函数有可能抛出错误
}

当函数可能能抛出错误消息时, 应该在表达式中前置 try 关键词

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
do {
try canThrowAnError()
// 没有错误消息抛出
} catch {
// 有一个错误消息抛出
}
// 捕获具体的异常
do {
try makeASandwich()
eatASandwich()
} catch SandwichError.outOfCleanDishes {
washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
buyGroceries(ingredients)
}

断言

1
2
3
let age = -3
assert(age >= 0, "A person's age cannot be less than zero")
// 因为 age < 0,所以断言会触发

参考文献