前言
Swift提供了多種流程控制結(jié)構(gòu),包括可以多次執(zhí)行任務(wù)的while循環(huán),基于特定條件選擇執(zhí)行不同代碼分支的if、guard和switch語句,還有控制流程跳轉(zhuǎn)到其他代碼位置的break和continue語句。
Swift 還提供了for-in循環(huán),用來更簡單地遍歷數(shù)組(Array),字典(Dictionary),區(qū)間(Range),字符串(String)和其他序列類型。
Swift 的switch語句比 C 語言中更加強(qiáng)大。case 還可以匹配很多不同的模式,包括范圍匹配,元組(tuple)和特定類型匹配。switch語句的 case 中匹配的值可以聲明為臨時常量或變量,在 case 作用域內(nèi)使用,也可以配合where來描述更復(fù)雜的匹配條件。
For-In 循環(huán)
可以使用 for-in 循環(huán)來遍歷一個集合中的所有元素,例如數(shù)組中的元素、范圍內(nèi)的數(shù)字或者字符串中的字符。
使用 for-in 遍歷一個數(shù)組所有元素
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
print("Hello, \(name)!")
}
// Hello, Anna!
// Hello, Alex!
// Hello, Brian!
// Hello, Jack!
你也可以通過遍歷一個字典來訪問它的鍵值對
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
print("\(animalName)s have \(legCount) legs")
}
// ants have 6 legs
// spiders have 8 legs
// cats have 4 legs
for-in 循環(huán)還可以使用數(shù)字范圍
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
使用 stride(from:to:by:) 函數(shù)跳過不需要的標(biāo)記。 后面的區(qū)間未閉合
let minuteInterval = 5
for tickMark in stride(from: 0, to: 60, by: minuteInterval) {
// 每5分鐘渲染一個刻度線 (0, 5, 10, 15 ... 45, 50, 55)
}
在閉區(qū)間使用 stride(from:through:by:) 起到同樣作用: 后面的區(qū)間閉合
let hours = 12
let hourInterval = 3
for tickMark in stride(from: 3, through: hours, by: hourInterval) {
// 每3小時渲染一個刻度線 (3, 6, 9, 12)
}
While 循環(huán)
while循環(huán)會一直運(yùn)行一段語句直到條件變成false。這類循環(huán)適合使用在第一次迭代前,迭代次數(shù)未知的情況下。Swift 提供兩種while循環(huán)形式
while循環(huán),每次在循環(huán)開始時計算條件是否符合
repeat-while循環(huán),每次在循環(huán)結(jié)束時計算條件是否符合。
While
while condition {
statements
}
Repeat-While
repeat {
statements
} while condition
條件語句
根據(jù)特定的條件執(zhí)行特定的代碼通常是十分有用的。當(dāng)錯誤發(fā)生時,你可能想運(yùn)行額外的代碼;或者,當(dāng)值太大或太小時,向用戶顯示一條消息。要實(shí)現(xiàn)這些功能,你就需要使用條件語句。
Swift 提供兩種類型的條件語句:if語句和switch語句。通常,當(dāng)條件較為簡單且可能的情況很少時,使用if語句。而switch語句更適用于條件較復(fù)雜、有更多排列組合的時候。并且switch在需要用到模式匹配(pattern-matching)的情況下會更有用。
if
if語句最簡單的形式就是只包含一個條件,只有該條件為true時,才執(zhí)行相關(guān)代碼:
var temperatureInFahrenheit = 30
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
}
// 輸出 "It's very cold. Consider wearing a scarf."
Switch
switch語句會嘗試把某個值與若干個模式(pattern)進(jìn)行匹配。根據(jù)第一個匹配成功的模式,switch語句會執(zhí)行對應(yīng)的代碼。當(dāng)有可能的情況較多時,通常用switch語句替換if語句
匹配類型豐富
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")
}
// 輸出 "The last letter of the alphabet"
不存在隱式的貫穿:不需要在case分支中顯式地使用break語句
復(fù)合匹配
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":
print("The letter A")
default:
print("Not the letter A")
}
// 輸出 "The letter A
區(qū)間匹配
case 分支的模式也可以是一個值的區(qū)間。下面的例子展示了如何使用區(qū)間匹配來輸出任意數(shù)字對應(yīng)的自然語言格式:
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語句中測試多個值。元組中的元素可以是值,也可以是區(qū)間。另外,使用下劃線(_)來匹配所有可能的值。
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分支體內(nèi)使用 —— 這種行為被稱為值綁定(value binding),因?yàn)槠ヅ涞闹翟赾ase分支體內(nèi),與臨時的常量或變量綁定。
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語句來判斷額外的條件。
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"
控制轉(zhuǎn)移語句
控制轉(zhuǎn)移語句改變你代碼的執(zhí)行順序,通過它可以實(shí)現(xiàn)代碼的跳轉(zhuǎn)。Swift 有五種控制轉(zhuǎn)移語句:
continue. break. fallthrough. return. throw
Continue
continue語句告訴一個循環(huán)體立刻停止本次循環(huán),重新開始下次循環(huán)。就好像在說“本次循環(huán)我已經(jīng)執(zhí)行完了”,但是并不會離開整個循環(huán)體。
let puzzleInput = "great minds think alike"
var puzzleOutput = ""
for character in puzzleInput {
switch character {
case "a", "e", "i", "o", "u", " ":
continue
default:
puzzleOutput.append(character)
}
}
print(puzzleOutput)
// 輸出 "grtmndsthnklk"
Break
break語句會立刻結(jié)束整個控制流的執(zhí)行。break 可以在 switch 或循環(huán)語句中使用,用來提前結(jié)束switch或循環(huán)語句。
注意: 當(dāng)一個switch分支僅僅包含注釋時,會被報編譯時錯誤。注釋不是代碼語句而且也不能讓switch分支達(dá)到被忽略的效果。你應(yīng)該使用break來忽略某個分支。
let numberSymbol: Character = "三" // 簡體中文里的數(shù)字 3
var possibleIntegerValue: Int?
switch numberSymbol {
case "1", "?", "一", "?":
possibleIntegerValue = 1
case "2", "?", "二", "?":
possibleIntegerValue = 2
case "3", "?", "三", "?":
possibleIntegerValue = 3
case "4", "?", "四", "?":
possibleIntegerValue = 4
default:
break
}
if let integerValue = possibleIntegerValue {
print("The integer value of \(numberSymbol) is \(integerValue).")
} else {
print("An integer value could not be found for \(numberSymbol).")
}
// 輸出 "The integer value of 三 is 3."
貫穿
在 Swift 里,switch語句不會從上一個 case 分支跳轉(zhuǎn)到下一個 case 分支中。相反,只要第一個匹配到的 case 分支完成了它需要執(zhí)行的語句,整個switch代碼塊完成了它的執(zhí)行。
如果你確實(shí)需要 C 風(fēng)格的貫穿的特性,你可以在每個需要該特性的 case 分支中使用fallthrough關(guān)鍵字。下面的例子使用fallthrough來創(chuàng)建一個數(shù)字的描述語句。
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."
fallthrough關(guān)鍵字不會檢查它下一個將會落入執(zhí)行的 case 中的匹配條件。fallthrough簡單地使代碼繼續(xù)連接到下一個 case 中的代碼,這和 C 語言標(biāo)準(zhǔn)中的switch語句特性是一樣的。
帶標(biāo)簽的語句
你可以使用標(biāo)簽(statement label)來標(biāo)記一個循環(huán)體或者條件語句,對于一個條件語句,你可以使用break加標(biāo)簽的方式,來結(jié)束這個被標(biāo)記的語句。對于一個循環(huán)語句,你可以使用break或者continue加標(biāo)簽,來結(jié)束或者繼續(xù)這條被標(biāo)記語句的執(zhí)行。
聲明一個帶標(biāo)簽的語句是通過在該語句的關(guān)鍵詞的同一行前面放置一個標(biāo)簽,作為這個語句的前導(dǎo)關(guān)鍵字(introducor keyword),并且該標(biāo)簽后面跟隨一個冒號。下面是一個針對while循環(huán)體的標(biāo)簽語法,同樣的規(guī)則適用于所有的循環(huán)體和條件語句。
gameLoop: while square != finalSquare {
diceRoll += 1
if diceRoll == 7 { diceRoll = 1 }
switch square + diceRoll {
case finalSquare:
// 骰子數(shù)剛好使玩家移動到最終的方格里,游戲結(jié)束。
break gameLoop
case let newSquare where newSquare > finalSquare:
// 骰子數(shù)將會使玩家的移動超出最后的方格,那么這種移動是不合法的,玩家需要重新擲骰子
continue gameLoop
default:
// 合法移動,做正常的處理
square += diceRoll
square += board[square]
}
}
print("Game over!")
提前退出
像if語句一樣,guard的執(zhí)行取決于一個表達(dá)式的布爾值。我們可以使用guard語句來要求條件必須為真時,以執(zhí)行g(shù)uard語句后的代碼。不同于if語句,一個guard語句總是有一個else從句,如果條件不為真則執(zhí)行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(["name": "John"])
// 輸出 "Hello John!"
// 輸出 "I hope the weather is nice near you."
greet(["name": "Jane", "location": "Cupertino"])
// 輸出 "Hello Jane!"
// 輸出 "I hope the weather is nice in Cupertino."
如果guard語句的條件被滿足,則繼續(xù)執(zhí)行g(shù)uard語句大括號后的代碼。將變量或者常量的可選綁定作為guard語句的條件,都可以保護(hù)guard語句后面的代碼。
如果條件不被滿足,在else分支上的代碼就會被執(zhí)行。這個分支必須轉(zhuǎn)移控制以退出guard語句出現(xiàn)的代碼段。它可以用控制轉(zhuǎn)移語句如return,break,continue或者throw做這件事,或者調(diào)用一個不返回的方法或函數(shù),例如fatalError()。
檢測 API 可用性
Swift內(nèi)置支持檢查 API 可用性,這可以確保我們不會在當(dāng)前部署機(jī)器上,不小心地使用了不可用的API。
編譯器使用 SDK 中的可用信息來驗(yàn)證我們的代碼中使用的所有 API 在項(xiàng)目指定的部署目標(biāo)上是否可用。如果我們嘗試使用一個不可用的 API,Swift 會在編譯時報錯。
我們在if或guard語句中使用可用性條件(availability condition)去有條件的執(zhí)行一段代碼,來在運(yùn)行時判斷調(diào)用的API是否可用。編譯器使用從可用性條件語句中獲取的信息去驗(yàn)證,在這個代碼塊中調(diào)用的 API 是否可用。
if #available(iOS 10, macOS 10.12, *) {
// 在 iOS 使用 iOS 10 的 API, 在 macOS 使用 macOS 10.12 的 API
} else {
// 使用先前版本的 iOS 和 macOS 的 API
}
以上可用性條件指定,if語句的代碼塊僅僅在 iOS 10 或 macOS 10.12 及更高版本才運(yùn)行。最后一個參數(shù),*,是必須的,用于指定在所有其它平臺中,如果版本號高于你的設(shè)備指定的最低版本,if語句的代碼塊將會運(yùn)行。