控制流
[TOC]
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!
你也可以通過遍歷一個字典來訪問它的鍵值對。遍歷字典時,字典的每項(xiàng)元素會以 (key, value)
元組的形式返回,你可以在 for-in
循環(huán)中使用顯式的常量名稱來解讀 (key, value)
元組。下面的例子中,字典的鍵聲明會為 animalName
常量,字典的值會聲明為 legCount
常量:
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ù)字范圍。下面的例子用來輸出乘法表的一部分內(nèi)容:
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
例子中用來進(jìn)行遍歷的元素是使用閉區(qū)間操作符(...
)表示的從 1
到 5
的數(shù)字區(qū)間。index
被賦值為閉區(qū)間中的第一個數(shù)字(1
),然后循環(huán)中的語句被執(zhí)行一次。在本例中,這個循環(huán)只包含一個語句,用來輸出當(dāng)前 index
值所對應(yīng)的乘 5 乘法表的結(jié)果。該語句執(zhí)行后,index
的值被更新為閉區(qū)間中的第二個數(shù)字(2
),之后 print(_:separator:terminator:)
函數(shù)會再執(zhí)行一次。整個過程會進(jìn)行到閉區(qū)間結(jié)尾為止。
上面的例子中,index
是一個每次循環(huán)遍歷開始時被自動賦值的常量。這種情況下,index
在使用前不需要聲明,只需要將它包含在循環(huán)的聲明中,就可以對其進(jìn)行隱式聲明,而無需使用 let
關(guān)鍵字聲明。
如果你不需要區(qū)間序列內(nèi)每一項(xiàng)的值,你可以使用下劃線( _
)替代變量來忽略這個值:
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"
這個例子計(jì)算 base 這個數(shù)的 power 次冪(本例中,是 3
的 10
次冪),從 1
( 3
的 0
次冪)開始做 3
的乘法, 進(jìn)行 10
次,使用 1
到 10
的閉區(qū)間循環(huán)。這個計(jì)算并不需要知道每一次循環(huán)中計(jì)數(shù)器具體的值,只需要執(zhí)行了正確的循環(huán)次數(shù)即可。下劃線符號 _
(替代循環(huán)中的變量)能夠忽略當(dāng)前值,并且不提供循環(huán)遍歷時對值的訪問。
在某些情況下,你可能不想使用閉區(qū)間,包括兩個端點(diǎn)。想象一下,你在一個手表上繪制分鐘的刻度線。總共 60
個刻度,從 0
分開始。使用半開區(qū)間運(yùn)算符( ..<
)來表示一個左閉右開的區(qū)間。
let minutes = 60
for tickMark in 0..<minutes {
// 每一分鐘都渲染一個刻度線
}
一些用戶可能在其UI中可能需要較少的刻度,他們可以每五分鐘作為一個刻度。使用 stride(form:to:by:)
函數(shù)跳過不需要的標(biāo)記。
let minuteInterval = 5
for tickMark in stride(form: 0, to: minutes, by: minuteInterval) {
// 每5分鐘渲染一個刻度線 (0, 5, 10, 15 ... 45, 50, 55)
}
可以在閉區(qū)間使用 stride(form:through:by:)
起到同樣的作用:
let hours = 12
let hourInterval = 3
for tickMark in stride(form: 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)開始時計(jì)算條件是否符合; -
repeat-while
循環(huán),每次在循環(huán)結(jié)束時計(jì)算條件是否符合。
while
while
循環(huán)從計(jì)算一個條件開始。如果條件為 true
,則會重復(fù)運(yùn)行一段語句,直到條件變?yōu)?false
。
下面是 while
循環(huán)的一般格式:
while condition {
// statements
}
repeat-while
while
循環(huán)的另外一種形式是 repeat-while
,它和 while
的區(qū)別是在判斷循環(huán)條件之前,先執(zhí)行一次循環(huán)代碼塊。然后重復(fù)循環(huán)直到條件為 false
。
下面是 repeat-while
循環(huán)的一般格式:
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
在需要用到模式匹配的情況下會更有用。
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."
上面的例子會判斷溫度是否小于等于 32 華氏度(水的冰點(diǎn))。如果是,則打印一條消息;否則,不打印任何消息,繼續(xù)執(zhí)行if
塊后面的代碼。
當(dāng)然,if
語句運(yùn)行二選一執(zhí)行,叫做 else
從句。也就是當(dāng)條件為 false
時,執(zhí)行 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."
顯然,這兩條分支中總有一條會被執(zhí)行。由于溫度已升至 40 華氏度,不算太冷,沒必要再圍圍巾。因此,else
分支就被觸發(fā)了。
你可以把多個 if
語句鏈接在一起,來實(shí)現(xiàn)更多分支:
temperatureInFahrenheit = 90
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 {
print("It's really warm. Don't forget to wear sunscreen.")
} else {
print("It's not that cold. Wear a t-shirt.")
}
// 輸出 "It's really warm. Don't forget to wear sunscreen."
在上面的例子中,額外的if
語句用于判斷是不是特別熱。而最后的else
語句被保留了下來,用于打印既不冷也不熱時的消息。
實(shí)際上,當(dāng)不需要完整判斷情況的時候,最后的 else
語句是可選的:
temperatureInFahrenheit = 72
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 {
print("It's really warm. Don't forget to wear sunscreen.")
}
在這個例子中,由于既不冷也不熱,所以不會觸發(fā)if
或else if
分支,也就不會打印任何消息。
switch
switch
語句會嘗試把某個值與若干個模式(pattern)進(jìn)行匹配。根據(jù)第一個匹配成功的模式,swicth
語句會執(zhí)行對應(yīng)的代碼。當(dāng)有可能的情況較多時,通常用 switch
語句替換 if
語句。
swicth
語句最簡單的形式就是把某個值與一個或若干個相同類型的值作比較:
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
}
swicth
語句由多個 case
構(gòu)成,每個由 case
關(guān)鍵字開始,為了匹配某些更特定的值,Swift提供了幾種方法來進(jìn)行更復(fù)雜的模式匹配。
與 if
語句類型,每一個 case
都是代碼執(zhí)行的一條分支。switch
語句會決定哪一條分支應(yīng)該被執(zhí)行,這個流程被稱作根據(jù)給定的值切換(switching)。
swicth
語句必須是完備的。這就是說,每一個可能的值都必須至少有一個 case
分支與之對應(yīng)。在某些不可能涵蓋所有值的情況下,你可以使用默認(rèn)(default
)分支來涵蓋其它所有沒有對應(yīng)的值,這個分支必須在 swicth
語句的最后面。
下面的例子使用 switch
語句來匹配一個名為 someCharacter
的小寫字符:
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 分支用于匹配第一個英文字母a
,第二個 case 分支用于匹配最后一個字母z
。 因?yàn)?code>switch語句必須有一個case分支用于覆蓋所有可能的字符,而不僅僅是所有的英文字母,所以switch語句使用default
分支來匹配除了a
和z
外的所有值,這個分支保證了swith語句的完備性。
不存在隱式貫穿
與C和Objective-C中的 Switch
語句不同,在Swift中,當(dāng)匹配的 case
分支中的代碼執(zhí)行完畢后,程序會終止 swicth
語句,而不會繼續(xù)執(zhí)行下一個 case
分支。這也就是說,不需要在 case 分支中顯式地使用 break
語句。這使得 swicth
語句更安全、更易用,也避免了因忘記寫 break
語句而產(chǎn)生的錯誤。
注意:雖然在switch中,
break
不是必須的,但你依然可以在case
分支中的代碼之前完畢前使用break
跳出。
每一個 case
分支都必須包含至少一條語句。像下面這樣書寫代碼是無效的,因?yàn)榈谝粋€ case
分支是空的:
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a": //無效,這個分支下面沒有語句
case "A":
print("The letter A")
default:
print("Not the letter A")
}
// 這段代碼會報(bào)編譯錯誤
不像 C 語言里的switch
語句,在 Swift 中,switch
語句不會一起匹配"a"
和"A"
。相反的,上面的代碼會引起編譯期錯誤:case "a": 不包含任何可執(zhí)行語句
——這就避免了意外地從一個 case 分支貫穿到另外一個,使得代碼更安全、也更直觀。
為了讓單個 case
同時匹配 a
和 A
,可以將這兩個值組合成一個符合匹配,并且用逗號分開:
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A" :
print("The letter A")
default:
print("Not 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."
在上例中,approximateCount
在一個switch
聲明中被評估。每一個case
都與之進(jìn)行比較。因?yàn)?code>approximateCount落在了 12 到 100 的區(qū)間,所以naturalCount
等于"dozens of"
值,并且此后的執(zhí)行跳出了switch
語句。
元組
我們可以使用元組在同一個 switch
語句中測試多個值。元組中的元素可以是值,也可以是區(qū)間。另外,使用下劃線(_
)來匹配所有可能的值。
下面的例子展示了如何使用一個 (Int, Int)
類型的元組來分類下圖中的點(diǎn)(x, y):
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
print("\(sonePoint) is at the orgin")
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"
[圖片上傳失敗...(image-9f4c18-1519906684756)]
在上面的例子中,switch
語句會判斷某個點(diǎn)十分是在原點(diǎn)(0, 0),是否在紅色的x軸上,是否在橘黃色的y軸上,是否在一個以原點(diǎn)為中心的4x4藍(lán)色矩形里,或者這個矩形外面。
不像C語言,Swift允許多個 case
匹配同一個值。實(shí)際上,在這個例子中,點(diǎn)(0, 0)客廳匹配所有的四個case。但是如果存在多個匹配,那么只會執(zhí)行第一個被匹配到的 case
分支??紤]點(diǎn)(0, 0)會首先匹配 case (0, 0)
,因此剩下的能夠匹配的分支都會被忽視掉。
值綁定
case
分支允許將匹配的值聲明為臨時常量或變量,并且在 case
分支體內(nèi)使用 - - 這種行為被稱為值綁定,因?yàn)槠ヅ涞闹翟?case
分支體內(nèi),與臨時的常量或變量綁定。
下面的例子將下圖中的點(diǎn)(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 an y value of \(y)")
case let (x, y):
print("somewhere else at (\(x), \(y))")
}
// 輸出 on the x-axis with an x value of 2
[圖片上傳失敗...(image-7909bc-1519906684756)]
在上面的例子中,switch
語句會判斷某個點(diǎn)是否在紅色的x軸上,是否在橘黃色的y軸上,或者不在坐標(biāo)軸上。
這三個 case 都聲明了常量x
和y
的占位符,用于臨時獲取元組anotherPoint
的一個或兩個值。
- 第一個 case ——
case (let x, 0)
將匹配一個縱坐標(biāo)為0
的點(diǎn),并把這個點(diǎn)的橫坐標(biāo)賦給臨時的常量x
。 - 第二個 case ——
case (0, let y)
將匹配一個橫坐標(biāo)為0
的點(diǎn),并把這個點(diǎn)的縱坐標(biāo)賦給臨時的常量y
。
一旦聲明了這些臨時的常量,它們就可以在其對應(yīng)的 case 分支里使用。在這個例子中,它們用于打印給定點(diǎn)的類型。
請注意,這個switch
語句不包含默認(rèn)分支。這是因?yàn)樽詈笠粋€ case ——case let(x, y)
聲明了一個可以匹配余下所有值的元組。這使得switch
語句已經(jīng)完備了,因此不需要再書寫默認(rèn)分支。
where
case
分支的模式可以使用 where
語句來判斷額外的條件。
下面的例子把下圖中的點(diǎn)(x, y)進(jìn)行了分類:
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
[圖片上傳失敗...(image-aff8da-1519906684756)]
在上面的例子中,switch
語句會判斷某個點(diǎn)是否在綠色的對角線 x == y
上,是否在紫色的對角線 x == -y
上,或者不在對角線上。
這三個 case
都聲明了常量 x
和 y
的占位符,用于臨時獲取元組的兩個值。這兩個常量被用作 where
語句的一部分,從而創(chuàng)建一個動態(tài)的過濾器(filter)。當(dāng)且僅當(dāng) where
語句的條件為 true
時,匹配到的 case
分支才會被執(zhí)行。
就像值綁定中的例子,由于最后一個 case
分支匹配了余下所有可能的值,switch
語句就已經(jīng)完備了,因此不需要再書寫默認(rèn)分支。
復(fù)合匹配
當(dāng)多個條件可以使用同一種方法來處理時,可以將這幾種可能放在同一個 case
后面,并且用逗號隔開。當(dāng) 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
個switch
語句中的第一個case,匹配了英語中的五個小寫元音字母。相似的,第二個case匹配了英語中所有的小寫輔音字母。最終,default
分支匹配了其它所有字符。
復(fù)合匹配同樣可以包含值綁定。復(fù)合匹配里所有的匹配模式,都必須包含相同的值綁定,并且每一個綁定都必須獲取到相同類型的值。這保證了,無論復(fù)合匹配中的哪個模式發(fā)生了匹配,分支體內(nèi)的代碼,都能獲取到綁定的值,并且綁定的值都有一樣的類型。
let stillAnotherPoint = (9, 0)
switch stillAnotherPoint {
case (let distance, 0), (0, let distance):
print("On an axis, \(distance) form the origin")
default:
print("Not on an axis")
}
// 輸出 On an axis, 9 from the origin
上面的case有兩個模式:(let distance, 0)
匹配了在x軸上的值,(0, let distance)
匹配了在y軸上的值。兩個模式都綁定了 distance
,并且distance
在兩種模式下,都是整型 - - 這意味著分支體內(nèi)的代碼,只要 case
匹配,都可以獲取到 distance
值。
控制轉(zhuǎn)移語句
控制轉(zhuǎn)移語句可以改變你代碼的執(zhí)行順序,通過它可以實(shí)現(xiàn)代碼的跳轉(zhuǎn)。Swift有五種控制轉(zhuǎn)移語句:
- continue
- break
- fallthrough
- return
- throw
我們將會在下面討論continue
、break
和fallthrough
語句。return
語句將會在函數(shù)章節(jié)討論,throw
語句會在錯誤拋出章節(jié)討論。
continue
continue
語句告訴一個循環(huán)體立刻停止本次循環(huán),重新開始下次循環(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
上面的代碼中,只要匹配到元音字母或者空格字符,就調(diào)用 continue
語句,使本次循環(huán)結(jié)束,重新開始下次循環(huán)。這種行為使 switch
匹配到元音字母和空格字符時不做處理。
break
break
語句會立刻結(jié)束整個控制流的執(zhí)行。break
可以在 switch
或者循環(huán)語句中使用,用來提前結(jié)束 switch
或循環(huán)語句。
循環(huán)語句中的 break
當(dāng)在一個循環(huán)體中使用 break
時,會立刻中斷該循環(huán)體的執(zhí)行,然后跳轉(zhuǎn)到表示循環(huán)體結(jié)束的大括號(}
)后的第一行代碼。不會再有本次循環(huán)的代碼被執(zhí)行,也不會再有下次的循環(huán)產(chǎn)生。
switch語句中的 break
當(dāng)在一個 switch
代碼塊中使用 break
時,會立即中斷該 switch
代碼塊的執(zhí)行,并且跳轉(zhuǎn)到表示 switch
代碼塊結(jié)束的大括號(}
)后的第一行代碼。
這種特性可以被用來匹配或者忽略一個或多個分支。因?yàn)镾wift的 switch
需要包含所有的分支而且不允許有為空的分支,有時為了使你的意圖更明顯,需要特意匹配或者忽略某個分支。那么當(dāng)你想忽略某個分支時,可以在該分支內(nèi)寫上 break
語句。當(dāng)那個分支被匹配到時,分支內(nèi)的 break
語句立即結(jié)束 switch
代碼塊。
注意:當(dāng)一個
switch
分支僅僅包含注釋時,會被報(bào)編譯時錯誤。注釋不是代碼語句而且也不能讓switch
分支達(dá)到被忽略的效果。你應(yīng)該使用break
來忽略某個分支。
下面的例子通過switch
來判斷一個Character
值是否代表下面四種語言之一。為了簡潔,多個值被包含在了同一個分支情況中。
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."
這個例子檢查numberSymbol
是否是拉丁,阿拉伯,中文或者泰語中的1
到4
之一。如果被匹配到,該switch
分支語句給Int?
類型變量possibleIntegerValue
設(shè)置一個整數(shù)值。
當(dāng)switch
代碼塊執(zhí)行完后,接下來的代碼通過使用可選綁定來判斷possibleIntegerValue
是否曾經(jīng)被設(shè)置過值。因?yàn)槭强蛇x類型的緣故,possibleIntegerValue
有一個隱式的初始值nil
,所以僅僅當(dāng)possibleIntegerValue
曾被switch
代碼塊的前四個分支中的某個設(shè)置過一個值時,可選的綁定才會被判定為成功。
在上面的例子中,想要把Character
所有的的可能性都枚舉出來是不現(xiàn)實(shí)的,所以使用default
分支來包含所有上面沒有匹配到字符的情況。由于這個default
分支不需要執(zhí)行任何動作,所以它只寫了一條break
語句。一旦落入到default
分支中后,break
語句就完成了該分支的所有代碼操作,代碼繼續(xù)向下,開始執(zhí)行if let
語句。
貫穿(fallthrough)
在Swift里,switch
語句不會從上一個 case
分支跳轉(zhuǎn)到下一個 case
分支中。相反,只要第一個匹配到的 case
分支完成了它需要執(zhí)行的語句,整個 switch
代碼塊完成了它的執(zhí)行。相比之下,C語言需要你顯式地插入 break
語句到每個 case
分支的末尾來阻止自動落入到下一個 case
分支中。Swift的這種避免默認(rèn)落入到下一個分支的特性意味著它的 switch
功能要比C語言的更加清晰和可預(yù)測,可以避免無意識地執(zhí)行多個 case
分支而引發(fā)錯誤。
如果你確實(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."
這個例子定義了一個String
類型的變量description
并且給它設(shè)置了一個初始值。函數(shù)使用switch
邏輯來判斷integerToDescribe
變量的值。當(dāng)integerToDescribe
的值屬于列表中的質(zhì)數(shù)之一時,該函數(shù)在description
后添加一段文字,來表明這個數(shù)字是一個質(zhì)數(shù)。然后它使用fallthrough
關(guān)鍵字來“貫穿”到default
分支中。default
分支在description
的最后添加一段額外的文字,至此switch
代碼塊執(zhí)行完了。
如果integerToDescribe
的值不屬于列表中的任何質(zhì)數(shù),那么它不會匹配到第一個switch
分支。而這里沒有其他特別的分支情況,所以integerToDescribe
匹配到default
分支中。
當(dāng)switch
代碼塊執(zhí)行完后,使用print(_:separator:terminator:)
函數(shù)打印該數(shù)字的描述。在這個例子中,數(shù)字5
被準(zhǔn)確的識別為了一個質(zhì)數(shù)。
注意:
fallthrough
關(guān)鍵字不會檢查它下一個將會落入執(zhí)行的case
中的匹配條件。fallthrough
簡單地使代碼繼續(xù)連接到下一個case
中的代碼,這和C語言標(biāo)準(zhǔn)中的switch
語句特性是一樣的。
帶標(biāo)簽的語句
在 Swift 中,你可以在循環(huán)體和條件語句中嵌套循環(huán)體和條件語句來創(chuàng)造復(fù)雜的控制流結(jié)構(gòu)。并且,循環(huán)體和條件語句都可以使用break
語句來提前結(jié)束整個代碼塊。因此,顯式地指明break
語句想要終止的是哪個循環(huán)體或者條件語句,會很有用。類似地,如果你有許多嵌套的循環(huán)體,顯式指明continue
語句想要影響哪一個循環(huán)體也會非常有用。
為了實(shí)現(xiàn)這個目的,你可以使用標(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)體和條件語句。
label name
: whilecondition
{statements
}
下面的例子是前面章節(jié)中蛇和梯子的適配版本,在此版本中,我們將使用一個帶有標(biāo)簽的while
循環(huán)體中調(diào)用break
和continue
語句。這次,游戲增加了一條額外的規(guī)則:
- 為了獲勝,你必須剛好落在第 25 個方塊中。
如果某次擲骰子使你的移動超出第 25 個方塊,你必須重新擲骰子,直到你擲出的骰子數(shù)剛好使你能落在第 25 個方塊中。
游戲的棋盤和之前一樣:
[圖片上傳失敗...(image-9483c6-1519906684756)]
finalSquare
、board
、square
和diceRoll
值被和之前一樣的方式初始化:
let finalSquare = 25
var board = [Int](repeating: 0, count: finalSquare + 1)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
var square = 0
var diceRoll = 0
這個版本的游戲使用while
循環(huán)和switch
語句來實(shí)現(xiàn)游戲的邏輯。while
循環(huán)有一個標(biāo)簽名gameLoop
,來表明它是游戲的主循環(huán)。
該while
循環(huán)體的條件判斷語句是while square !=finalSquare
,這表明你必須剛好落在方格25中。
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!")
每次循環(huán)迭代開始時擲骰子。與之前玩家擲完骰子就立即移動不同,這里使用了switch
語句來考慮每次移動可能產(chǎn)生的結(jié)果,從而決定玩家本次是否能夠移動。
- 如果骰子數(shù)剛好使玩家移動到最終的方格里,游戲結(jié)束。
break gameLoop
語句跳轉(zhuǎn)控制去執(zhí)行while
循環(huán)體后的第一行代碼,意味著游戲結(jié)束。 - 如果骰子數(shù)將會使玩家的移動超出最后的方格,那么這種移動是不合法的,玩家需要重新擲骰子。
continue gameLoop
語句結(jié)束本次while
循環(huán),開始下一次循環(huán)。 - 在剩余的所有情況中,骰子數(shù)產(chǎn)生的都是合法的移動。玩家向前移動
diceRoll
個方格,然后游戲邏輯再處理玩家當(dāng)前是否處于蛇頭或者梯子的底部。接著本次循環(huán)結(jié)束,控制跳轉(zhuǎn)到while
循環(huán)體的條件判斷語句處,再決定是否需要繼續(xù)執(zhí)行下次循環(huán)。
注意: 如果上述的
break
語句沒有使用gameLoop
標(biāo)簽,那么它將會中斷switch
語句而不是while
循環(huán)。使用gameLoop
標(biāo)簽清晰的表明了break
想要中斷的是哪個代碼塊。 同時請注意,當(dāng)調(diào)用continue gameLoop
去跳轉(zhuǎn)到下一次循環(huán)迭代時,這里使用gameLoop
標(biāo)簽并不是嚴(yán)格必須的。因?yàn)樵谶@個游戲中,只有一個循環(huán)體,所以continue
語句會影響到哪個循環(huán)體是沒有歧義的。然而,continue
語句使用gameLoop
標(biāo)簽也是沒有危害的。這樣做符合標(biāo)簽的使用規(guī)則,同時參照旁邊的break gameLoop
,能夠使游戲的邏輯更加清晰和易于理解。
提前退出
像 if
語句一樣,guard
的執(zhí)行取決于一個表達(dá)式的布爾值。我們可以使用 guard
語句來要求條件必須為真時,以執(zhí)行 guard
語句后面的代碼。不同于 if
語句,一個 guard
語句總是有一個 else
從句,如果條件不為真則執(zhí)行 else
從句中的代碼。
func greet(person: [String: String]) {
guard let name = person["name"] else {
return
}
print("Hello \(name)")
grard 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í)行 guard
語句大括號后的代碼。將變量或者常量的可選綁定作為 guard
語句的條件,都可以保護(hù) guard
后面的代碼。
如果條件被被滿足,在 else
分支上的代碼就會被執(zhí)行。這個分支必須轉(zhuǎn)移控制以退出 guard
語句出現(xiàn)的代碼段。它可以用控制轉(zhuǎn)移語句如 return
,break
,continue
或者throw
做這件事,或者調(diào)用一個不返回的方法或函數(shù),例如fatalError()
。
相比于可以實(shí)現(xiàn)同樣功能的 if
語句,按需使用 guard
語句會提升我們代碼的可讀性。它可以使你的代碼連貫的被執(zhí)行而不需要將它包在 else
塊中,它可以使你在緊鄰條件判斷的地方,處理違規(guī)情況。
檢測API可用性
if #avilable(iOS 10.0, *) {
}