控制流

控制流

[TOC]

Swift提供了多種流程控制結(jié)構(gòu),包括可以多次執(zhí)行任務(wù)的while循環(huán),基于特定條件選擇執(zhí)行不同代碼分支的ifguardswitch語句,還有控制流程跳轉(zhuǎn)到其他代碼位置的breakcontinue語句。

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ū)間操作符(...)表示的從 15 的數(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 次冪(本例中,是 310 次冪),從 130 次冪)開始做 3 的乘法, 進(jìn)行 10 次,使用 110 的閉區(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ā)ifelse 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分支來匹配除了az外的所有值,這個分支保證了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 同時匹配 aA ,可以將這兩個值組合成一個符合匹配,并且用逗號分開:

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 都聲明了常量xy的占位符,用于臨時獲取元組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 都聲明了常量 xy 的占位符,用于臨時獲取元組的兩個值。這兩個常量被用作 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、breakfallthrough語句。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是否是拉丁,阿拉伯,中文或者泰語中的14之一。如果被匹配到,該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: while condition { statements }

下面的例子是前面章節(jié)中蛇和梯子的適配版本,在此版本中,我們將使用一個帶有標(biāo)簽的while循環(huán)體中調(diào)用breakcontinue語句。這次,游戲增加了一條額外的規(guī)則:

  • 為了獲勝,你必須剛好落在第 25 個方塊中。

如果某次擲骰子使你的移動超出第 25 個方塊,你必須重新擲骰子,直到你擲出的骰子數(shù)剛好使你能落在第 25 個方塊中。

游戲的棋盤和之前一樣:

[圖片上傳失敗...(image-9483c6-1519906684756)]

finalSquare、boardsquarediceRoll值被和之前一樣的方式初始化:

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, *) {
    
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,157評論 2 375

推薦閱讀更多精彩內(nèi)容

  • Swift 提供了類似 C 語言的流程控制結(jié)構(gòu),包括可以多次執(zhí)行任務(wù)的for和while循環(huán),基于特定條件選擇執(zhí)行...
    窮人家的孩紙閱讀 715評論 1 1
  • [The Swift Programming Language 中文版]本頁包含內(nèi)容: Swift提供了多種流程控...
    風(fēng)林山火閱讀 577評論 0 0
  • Swift提供了多種控制流聲明。包括while循環(huán)來多次執(zhí)行一個任務(wù);if,guard和switch聲明來根據(jù)確定...
    BoomLee閱讀 1,976評論 0 3
  • 長馬小記—42.195,唯認(rèn)真方能品 2015.10.18,長沙,人生的第2場馬拉松,以凈成績4.03.28完賽,...
    wood閱讀 420評論 1 3
  • 元認(rèn)知能力作為我們的最重要能力,一定要不斷的想辦法練習(xí)提升。那如何才能不斷提升我們的元認(rèn)知能力將是一個重要的...
    守望者_(dá)e0bd閱讀 306評論 0 1