Swift 提供了多种流程控制结构,包括可以多次执行任务的 while
循环,基于特定条件选择执行不同代码分支的 if
、guard
和 switch
语句,还有控制流程跳转到其他代码位置的 break
和 continue
语句。
For-In 循环
你可以使用 for-in
循环来遍历一个集合中的所有元素,例如数组中的元素、范围内的数字或者字符串中的字符。
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
print("Hello, (name)!")
}
// Hello, Anna!
// Hello, Alex!
// Hello, Brian!
// Hello, Jack!
你也可以通过遍历一个字典来访问它的键值对。遍历字典时,字典的每项元素会以 (key, value
) 元组的形式返回,你可以在 for-in
循环中使用显式的常量名称来解读 (key, value
) 元组。下面的例子中,字典的键会声明为 animalName
常量,字典的值会声明为 legCount
常量:
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
print("(animalName)s have (legCount) legs")
}
// cats have 4 legs
// ants have 6 legs
// spiders have 8 legs
for-in
循环还可以使用数字范围。下面的例子用来输出乘法表的一部分内容:
for index in 1...5 {
print("(index) times 5 is (index * 5)")
}
// 1 times 5 is 5
// 2 times 5 is 10
// 3 times 5 is 15
// 4 times 5 is 20
// 5 times 5 is 25
如果你不需要区间序列内每一项的值,你可以使用下划线(_
)替代变量名来忽略这个值:
let base = 3
let power = 10
var answer = 1
for _ in 1...power {
answer *= base
}
print("(base) to the power of (power) is (answer)")
// 输出“3 to the power of 10 is 59049”
使用半开区间运算符(..<
)来表示一个左闭右开的区间
let minutes = 60
for tickMark in 0..<minutes {
// 每一分钟都渲染一个刻度线(60次)
}
他们可以每 5 分钟作为一个刻度。使用 stride(from:to:by:)
函数跳过不需要的标记。
let minuteInterval = 5
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
// 每5分钟渲染一个刻度线(0, 5, 10, 15 ... 45, 50, 55)
}
可以在闭区间使用 stride(from:through:by:)
起到同样作用:
let hours = 12
let hourInterval = 3
for tickMark in stride(from: 3, through: hours, by: hourInterval) {
// 每3小时渲染一个刻度线(3, 6, 9, 12)
}
While 循环
while
循环会一直运行一段语句直到条件变成 false
。这类循环适合使用在第一次迭代前,迭代次数未知的情况下。Swift 提供两种 while
循环形式:
- while 循环,每次在循环开始时计算条件是否符合;
- repeat-while 循环,每次在循环结束时计算条件是否符合。
while
循环从计算一个条件开始。如果条件为 true,会重复运行一段语句,直到条件变为 false
。
下面是 while
循环的一般格式:
while condition {
statements
}
Repeat-While
while
循环的另外一种形式是 repeat-while
,它和 while
的区别是在判断循环条件之前,先执行一次循环的代码块。然后重复循环直到条件为 false
。
注意
Swift 语言的repeat-while
循环和其他语言中的do-while
循环是类似的。
下面是 repeat-while
循环的一般格式:
repeat {
statements
} while condition
条件语句
Swift 提供两种类型的条件语句:if
语句和 switch
语句。通常,当条件较为简单且可能的情况很少时,使用 if
语句。而 switch
语句更适用于条件较复杂、有更多排列组合的时候。并且 switch
在需要用到模式匹配(pattern-matching
)的情况下会更有用。
If
if
语句最简单的形式就是只包含一个条件,只有该条件为 true
时,才执行相关代码:
var temperatureInFahrenheit = 30
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
}
// 输出“It's very cold. Consider wearing a scarf.”
if
语句允许二选一执行,叫做 else
从句。也就是当条件为 false
时,执行 else
语句:
temperatureInFahrenheit = 40
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
} else {
print("It's not that cold. Wear a t-shirt.")
}
// 输出“It's not that cold. Wear a t-shirt.”
Switch
switch
语句会尝试把某个值与若干个模式(pattern
)进行匹配。根据第一个匹配成功的模式,switch
语句会执行对应的代码。当有可能的情况较多时,通常用 switch
语句替换 if
语句。
switch
语句最简单的形式就是把某个值与一个或若干个相同类型的值作比较:
switch
语句最简单的形式就是把某个值与一个或若干个相同类型的值作比较:
switch some value to consider {
case value 1:
respond to value 1
case value 2,
value 3:
respond to value 2 or 3
default:
otherwise, do something else
}
每一个 case
分支都必须包含至少一条语句。像下面这样书写代码是无效的,因为第一个 case
分支是空的:
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a": // 无效,这个分支下面没有语句
case "A":
print("The letter A")
default:
print("Not the letter A")
}
// 这段代码会报编译错误
为了让单个 case 同时匹配 a
和 A
,可以将这个两个值组合成一个复合匹配,并且用逗号分开:
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":
print("The letter A")
default:
print("Not the letter A")
}
// 输出“The letter A”
区间匹配
case
分支的模式也可以是一个值的区间。下面的例子展示了如何使用区间匹配来输出任意数字对应的自然语言格式:
let approximateCount = 62
let countedThings = "moons orbiting Saturn"
let naturalCount: String
switch approximateCount {
case 0:
naturalCount = "no"
case 1..<5:
naturalCount = "a few"
case 5..<12:
naturalCount = "several"
case 12..<100:
naturalCount = "dozens of"
case 100..<1000:
naturalCount = "hundreds of"
default:
naturalCount = "many"
}
print("There are (naturalCount) (countedThings).")
// 输出“There are dozens of moons orbiting Saturn.”
元组
我们可以使用元组在同一个 switch
语句中测试多个值。元组中的元素可以是值,也可以是区间。另外,使用下划线(_
)来匹配所有可能的值。
下面的例子展示了如何使用一个 (Int, Int)
类型的元组来分类下图中的点 (x, y)
:
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
print("(somePoint) is at the origin")
case (_, 0):
print("(somePoint) is on the x-axis")
case (0, _):
print("(somePoint) is on the y-axis")
case (-2...2, -2...2):
print("(somePoint) is inside the box")
default:
print("(somePoint) is outside of the box")
}
// 输出“(1, 1) is inside the box”
值绑定(Value Bindings)
case
分支允许将匹配的值声明为临时常量或变量,并且在 case
分支体内使用 —— 这种行为被称为值绑定(value binding
),因为匹配的值在 case
分支体内,与临时的常量或变量绑定。
下面的例子将下图中的点 (x, y)
,使用 (Int, Int)
类型的元组表示,然后分类表示:
let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
print("on the x-axis with an x value of (x)")
case (0, let y):
print("on the y-axis with a y value of (y)")
case let (x, y):
print("somewhere else at ((x), (y))")
}
// 输出“on the x-axis with an x value of 2”
Where
case
分支的模式可以使用 where
语句来判断额外的条件。
下面的例子把下图中的点 (x, y)
进行了分类:
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")
}
// 输出“(1, -1) is on the line x == -y”
复合型 Cases
当多个条件可以使用同一种方法来处理时,可以将这几种可能放在同一个 case
后面,并且用逗号隔开。当 case
后面的任意一种模式匹配的时候,这条分支就会被匹配。并且,如果匹配列表过长,还可以分行书写:
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")
}
// 输出“e is a vowel”
控制转移语句
控制转移语句改变你代码的执行顺序,通过它可以实现代码的跳转。Swift 有五种控制转移语句:
continue
break
fallthrough
return
throw
Continue
continue
语句告诉一个循环体立刻停止本次循环,重新开始下次循环。就好像在说“本次循环我已经执行完了”,但是并不会离开整个循环体。
Break
break
语句会立刻结束整个控制流的执行。break
可以在 switch
或循环语句中使用,用来提前结束 switch 或循环语句。
贯穿(Fallthrough)
fallthrough
关键字不会检查它下一个将会落入执行的 case
中的匹配条件。fallthrough
简单地使代码继续连接到下一个 case
中的代码,这和 C
语言标准中的 switch
语句特性是一样的。
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."
}
print(description)
// 输出“The number 5 is a prime number, and also an integer.”
带标签的语句
在 Swift 中,你可以在循环体和条件语句中嵌套循环体和条件语句来创造复杂的控制流结构。并且,循环体和条件语句都可以使用 break
语句来提前结束整个代码块。因此,显式地指明 break
语句想要终止的是哪个循环体或者条件语句,会很有用。类似地,如果你有许多嵌套的循环体,显式指明 continue
语句想要影响哪一个循环体也会非常有用。
为了实现这个目的,你可以使用标签(statement label
)来标记一个循环体或者条件语句,对于一个条件语句,你可以使用 break 加标签的方式,来结束这个被标记的语句。对于一个循环语句,你可以使用 break 或者 continue
加标签,来结束或者继续这条被标记语句的执行。
声明一个带标签的语句是通过在该语句的关键词的同一行前面放置一个标签,作为这个语句的前导关键字(introducor keyword
),并且该标签后面跟随一个冒号。下面是一个针对 while
循环体的标签语法,同样的规则适用于所有的循环体和条件语句。
label name: while condition {
statements
}
提前退出
像 if
语句一样,guard
的执行取决于一个表达式的布尔值。我们可以使用 guard
语句来要求条件必须为真时,以执行 guard
语句后的代码。不同于 if
语句,一个 guard
语句总是有一个 else
从句,如果条件不为真则执行 else
从句中的代码。
func greet(person: [String: String]) {
guard let name = person["name"] else {
return
}
print("Hello (name)!")
guard let location = person["location"] else {
print("I hope the weather is nice near you.")
return
}
print("I hope the weather is nice in (location).")
}
greet(person: ["name": "John"])
// 输出“Hello John!”
// 输出“I hope the weather is nice near you.”
greet(person: ["name": "Jane", "location": "Cupertino"])
// 输出“Hello Jane!”
// 输出“I hope the weather is nice in Cupertino.”
如果 guard
语句的条件被满足,则继续执行 guard
语句大括号后的代码。将变量或者常量的可选绑定作为 guard
语句的条件,都可以保护 guard
语句后面的代码。
如果条件不被满足,在 else
分支上的代码就会被执行。这个分支必须转移控制以退出 guard
语句出现的代码段。它可以用控制转移语句如 return、break、continue
或者 throw
做这件事,或者调用一个不返回的方法或函数,例如 fatalError()
。
检测 API 可用性
Swift 内置支持检查 API 可用性,这可以确保我们不会在当前部署机器上,不小心地使用了不可用的 API。
if #available(iOS 10, macOS 10.12, *) {
// 在 iOS 使用 iOS 10 的 API, 在 macOS 使用 macOS 10.12 的 API
} else {
// 使用先前版本的 iOS 和 macOS 的 API
}