5、Swift控制流
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
}