引入
前面学习Swift编写的代码, 都是从上向下依次执行。 我们做不到选择某些语句执行, 或者根据条件选择是否执行; 或者是学习集合类型的时候, 也没有办法很方便的, 将集合中所有的值都获取使用。要想实现这些目标,就离不开控制流。
For-In循环
使用for-in循环可以遍历一个序列。
遍历数组
let numbers = [3, 5, 7, 4, 2]
for item in numbers {
print(item)
}
运行结果
3 5 7 4 2
如果遍历数组的同时还需要当前值对应的索引, 可以使用 enumerated
方法。 enumerated
方法会返回一个由
整数索引和值构成的元组。
let numbers = [3, 5, 7, 4, 2]
for (index, value) in numbers.enumerated() {
print("索引:\(index),值:\(value)")
}
运行结果
索引:0, 值:3 索引:1, 值:5 索引:2, 值:7 索引:3, 值:4 索引:4, 值:2
遍历集合
let ids: Set = [10, 20, 30, 40, 50]
for id in ids {
print(id)
}
运行结果
30 10 40 50 20
注意集合是无序的。 如果需要按照特定顺序遍历集合, 可以使用 sorted
方法。
let ids: Set = [10, 20, 30, 40, 50]
for id in ids.sorted() {
print(id)
}
运行结果
10 20 30 40 50
遍历字典
遍历字典时,每次循环的是字典中的键值对。
let users = ["王力宏": 49, "陶喆": 54, "周杰伦": 46]
for (name, age) in users {
print("姓名:\(name), 年龄:\(age)")
}
运行结果
姓名:王力宏, 年龄:49 姓名:周杰伦, 年龄:46 姓名:陶喆, 年龄:54
遍历字典所有的键或值
如果只需要遍历字典中的键可以使用 keys
属性, 只要值使用 values
属性。
let users = ["王力宏": 49, "陶喆": 54, "周杰伦": 46]
print(users.keys)
for key in users.keys {
print(key)
}
print(users.values)
for value in users.values {
print(value)
}
运行结果
[“周杰伦”, “王力宏”, “陶喆”] 周杰伦 王力宏 陶喆
[46, 49, 54] 46 49 54
遍历字符串
使用for-in遍历字符串, 可以访问字符串的每个字符值。
let str = "hello world"
for letter in str {
print(letter)
}
运行结果
h e l l o
w o r l d
范围运算符
封闭范围运算符
a...b
运算符定义了a到b的范围, 其中包括a和b的值。 在对范围遍历时, 希望使用其中的所有值,可以使用for-in循环。
for item in 1...5 {
print(item)
}
运行结果
1 2 3 4 5
半开放范围运算符
a..<b
运算符定义了a到b的范围, 不包括b的值。 如果a的值等于b, 范围将是空的。
for item in 1..<5 {
print(item)
}
运行结果
1 2 3 4
单边范围
封闭范围运算符有另一种形式, 可用于向一个方向延伸的范围。
比如需要数组索引2以后的所有元素, 可以表示为 [2...]
。
let letters = ["a", "b", "c", "d", "e"]
for letter in letters[2...] { // 获取letters数组索引2及以后的值
print(letter)
}
print(" ")
for letter in letters[...3] { // 获取开始到索引3的值
print(letter)
}
print(" ")
for letter in letters[..<3] { // 获取开始到索引3的值, 不包括索引3
print(letter)
}
运行结果
c d e
a b c d
a b c
使用范围运算符循环指定次数
有了范围运算符, 想要循环固定次数就非常容易了。
let count = 10
for _ in 0..<count { // 既然固定次数,范围的值也就不用了,可以使用_
print("hello")
}
运行结果
hello hello hello hello hello hello hello hello hello hello
stride函数
某些场景下, 范围运算符也不够用。 比如需要以5为步进的值序列:5, 10, 15, 20… 。 我们可以使用stride函数。
// stride(from: 起始, to: 结束, by: 步进),不包括结束
for item in stride(from: 5, to: 30, by: 5) {
print(item)
}
print("")
// stride(from: 起始, through: 结束, by: 步进),包括结束
for item in stride(from: 5, through: 30, by: 5) {
print(item)
}
运行结果
5 10 15 20 25
5 10 15 20 25 30
While循环
while循环执行一组语句, 直到条件变为false。 如果不知道迭代的次数, 使用while循环就很合适。
Swift提供了两种while循环:
- while在每次循环开始时检查条件
- repeat-while在每次循环结束时检查条件
while
while循环以检查条件开始, 如果条件为真, 就执行一组语句; 如果条件为假, 就退出循环, 向下执行其他代码。
var start = 0 // 起始值
var total = 0 // 累加结果
let end = 100 // 终止值
while start <= end { // 当起始值小于等于终止值时,就执行话括号中的语句
total += start // 将当前的值累加到total上
start += 1 // 让当前的值自增1
}
print(total) // 输出结果,5050
Repeat-While
在检查循环条件之前, 先执行一次循环语句。 然后重复循环, 直到条件变为false。
// 使用repeat-while循环输出5,4,3,2,1
var number = 5
repeat {
print(number) // 输出当前值
number -= 1 // 将number减1
} while number > 0 // 检查条件,number>0就继续,否则退出循环
运行结果
5 4 3 2 1
条件语句
根据特定条件执行不同的代码。
If
单个if条件
最简单的语句就是只有一个if条件。 只有条件为真时, 才会执行一组语句。
print("begin")
let number = 5
if number > 0 {
print("bigger") // 当number>0条件为真时,才会执行这段代码
}
print("end")
运行结果
begin bigger end
尝试把number设置成负数, 看看结果。
if-else
如果根据条件成立与否,需要执行不同代码块, 可以使用if - else。
let isRainyDay = false
if isRainyDay {
print("正在下雨,请打伞") // isRainyDay为真执行
} else {
print("好天气,随便玩") // isRainyDay为假执行
}
运行结果
好天气,随便玩
if - else if - else
如果条件判断的结果不只两种, 可以通过 else if 加入更多分支。
let weather = "snowy" // weather记录当前天气
if weather == "sunny" { // if语句
print("大晴天,出去玩")
} else if weather == "rainy" { // 执行语句后面使用else if加入新分支
print("下雨天,慢慢玩")
} else if weather == "snowy" { // 执行语句后面使用else if加入新分支
print("下雪天,小心玩")
} else { // 上面的条件都不满足,就执行这里
print("未知天,不去玩")
}
运行结果
下雪天,小心玩
在多分支表达式中, 从上往下检查条件, 某个条件为真, 这个分支的值就会作为整个if表达式的值。
将if表达式的结果赋值给变量/常量
如果if表达式的结果不是输出, 而是需要保存到变量/常量中使用时, 也是可以的。
let age = 18
let isAdult = if age >= 18 {
true // 如果age大于等于18,就给isAdult赋值为true
} else {
false // 否则,赋值为false
}
print(isAdult) // true
如果不同分支返回的数据类型不同, 可以提供显式类型, 或者使用 as
做类型转换。
let age = 18
let isAdult: Bool? = if age >= 18 { // 给isAdult显式类型
true
} else {
nil // 不满足就返回nil
}
print(isAdult) // Optional(true)
let age = 18
let isAdult = if age >= 18 {
true
} else {
nil as Bool? // as类型转换
}
print(isAdult) // Optional(true)
可选绑定
使用可选绑定查找一个可选项是否包含值。 如果有值, 就将该值作为一个临时变量/常量使用。
let str = "123"
if let number = Int(str) {
print("访问转换成功的数字 \(number)")
} else {
print("看来转不出来")
}
运行结果
访问转换成功的数字 123
如果访问可选常量/变量包含的值以后, 不再需要原来的可选常量/变量, 可以使用相同名称。
let origin = "123"
let number = Int(origin)
if let number = number { // 新的名称和原来的名称一样
print(number)
}
if let number { // 上面这样的代码很常见,所以可以简写为这样
print(number)
}
运行结果
123 123
三元条件运算符
根据条件表达式的结果, 返回不同的值是一种很常见的场景。比如上面出现过的例子:
let age = 16
let isAdult = if age >= 18 {
true
} else {
false
}
print(isAdult) // false
正因为很常见, 所以有一个特殊的运算符处理这种情况。
三元条件运算符 条件 ? 表达式1 : 表达式2
。 如果条件为真, 对表达式1求值并返回结果, 否则对表达式2求值并返回结果。
上面的代码可以简写成这样:
let age = 16
let isAudlt = age >= 18 ? true : false
print(isAudlt) // false
Switch
switch语句会检查一个值, 并将其与可能匹配的模式进行比较, 找到第一个成功匹配的模式执行其代码块。
每个switch语句由多个可能的case组成, 每个case由case关键字开头。 case就是代码执行的一个单独分支。
switch语句应当是具有穷举性的, 即每一种可能性都要考虑到。 如果要涵盖不明确的值, 可以使用default关键字 表示默认情况。 default语句应该在switch的最后。
let weather = "sunny"
switch weather {
case "rainy":
print("雨天")
case "sunny":
print("晴天")
case "snowy":
print("雪天")
default:
print("未知")
}
运行结果
晴天
表达式形式
let weather = "sunny"
let messsage = switch weather {
case "rainy":
"雨天"
case "sunny":
"晴天"
case "snowy":
"雪天"
default:
"未知"
}
print(messsage) // 晴天
多个模式执行相同语句
如果多个case匹配后执行的语句相同, 可以放在一个case中, 使用逗号分隔。
let letter = "a"
let messsage = switch letter {
case "a", "A": // a 和 A 都会匹配到当前模式
"这是字母a或者A"
default:
"不是字母a或者A"
}
print(messsage) // 这是字母a或者A
区间匹配
可以检查switch的值是否包含在区间中。
let score = 85
let result = switch score {
case 90...100:
"best"
case 80..<90:
"good"
case 70..<80:
"fine"
case 60..<70:
"just so so"
default:
"bad"
}
print(result) // good
控制转移
控制转移语句通过将控制权从一段代码转移到另一段代码, 来改变代码的执行顺序。
Continue
continue语句停止当前循环, 立即开始下一次循环。
let singers = ["陶喆", "王力宏", "周杰伦", "林俊杰"]
// 遍历singers,播放每个人的歌曲,如果碰到周杰伦的歌,就跳过不放。
for singer in singers {
if singer == "周杰伦" {
continue; // 如果是周杰伦,立即停止,开始下一次循环。
}
print("播放\(singer)的歌曲.")
}
运行结果
播放陶喆的歌曲. 播放王力宏的歌曲. 播放林俊杰的歌曲.
Break
break语句立即结束整个控制流语句的执行。 可以用于退出循环语句和Switch语句。
let singers = ["陶喆", "王力宏", "周杰伦", "林俊杰"]
for singer in singers {
if singer == "王力宏" {
break; // 如果播放到王力宏的歌,就立即退出整个循环
}
print("播放\(singer)的歌曲.")
}
运行结果
播放陶喆的歌曲.
Fallthrough
在swift中, 当switch匹配到case以后, 会返回匹配结果。 如果需要匹配成功以后, 继续向下匹配, 可以使用
fallthrough
关键字。
let weather = "sunny"
switch weather {
case "rainy": // 不能匹配
print("雨天")
case "sunny": // 匹配成功
print("晴天") // 输出晴天
fallthrough // 因为fallthrough语句,继续执行下一个case
case "snowy":
print("雪天") // 输出雪天,没有fallthrough,退出switch
default:
print("未知")
} // 晴天
运行结果
晴天 雪天
标签语句
真实业务中, 会出现许多复杂的控制流, 让循环和条件相互嵌套。
如果存在嵌套, break 和 continue 会在当前结构块中生效。 如果想要操作的是外部循环和条件, 可以加上标签。
比如下面的九九乘法表
for i in 1...9 {
for j in 1...i {
// print函数的terminator设置输出以后的终止符,默认是\n换行
print("\(j) * \(i) = \(j * i)", terminator: " ")
}
print("")
}
运行结果
1 * 1 = 1 1 * 2 = 2 2 * 2 = 4 1 * 3 = 3 2 * 3 = 6 3 * 3 = 9 1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16 1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25 1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36 1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49 1 * 8 = 8 2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 5 * 8 = 40 6 * 8 = 48 7 * 8 = 56 8 * 8 = 64 1 * 9 = 9 2 * 9 = 18 3 * 9 = 27 4 * 9 = 36 5 * 9 = 45 6 * 9 = 54 7 * 9 = 63 8 * 9 = 72 9 * 9 = 81
我希望当j等于5的时候, 停止外部当前循环,并立即开始下一次。 这可以通过加上标签实现。
outer: for i in 1...9 {
for j in 1...i {
if j == 5 {
continue outer
}
print("\(j) * \(i) = \(j * i)", terminator: " ")
}
print("")
}
运行结果
1 * 1 = 1 1 * 2 = 2 2 * 2 = 4 1 * 3 = 3 2 * 3 = 6 3 * 3 = 9 1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16 1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 1 * 8 = 8 2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 1 * 9 = 9 2 * 9 = 18 3 * 9 = 27 4 * 9 = 36
延迟执行
可以使用 defer
推迟代码块的执行。 无论程序如果退出该作用域, defer内的代码始终会执行。
所以defer非常适合延缓必要操作的执行。 比如开始和结束事务, 打开和关闭文件等。
let filePath = "test.txt"
if filePath != "" {
print("打开文件")
defer {
print("关闭文件")
}
print("读取文件");
}
运行结果
打开文件 读取文件 关闭文件
如果存在多个defer代码块, 第一个defer将最后执行。
let filePath = "test.txt"
if filePath != "" {
print("打开文件")
defer {
print("操作完成")
}
defer {
print("关闭文件")
}
print("读取文件");
}
运行结果
打开文件 读取文件 关闭文件 操作完成
检查API可用性
Swift内置检查API可用性的支持, 确保不会在部署目标上使用不可用的API。
// 检查是否是iOS 15,macOS 14以上的版本
// 最后的*必需, 表示其他平台以最小部署目标执行
if #available(iOS 15, macOS 14, *) {
print("使用xxxAPI")
} else {
print("想想其他办法")
}
运行结果
使用xxxAPI
在使用带有保护语句的可用性条件时, 会完善该代码块中其他代码所使用的可用性信息。
@available(macOS 10.12, *)
struct ColorPreference {
var bestColor = "blue"
}
func chooseBestColor() -> String {
guard #available(macOS 10.12, *) else {
return "gray"
}
let colors = ColorPreference()
return colors.bestColor
}
下一章
控制流语句可以帮助我们实现复杂逻辑, 所有的应用都离不开它们。
下一章, 我们看看Swift中的函数是怎样的。