Swift5.2-控制流(中文文檔)

引言

繼續學習Swift文檔,從上一章節:集合類型,咱們學習了Swift集合相關的內容。如今,咱們學習Swift的控制流相關的內容。因爲篇幅較長,這裏分篇來記錄,接下來,開始吧!html

若是你已經熟悉並掌握了條件控制相關內容,請參閱下一章節:函數swift

控制流

Swift提供了多種控制流語句。這些包括while循環來屢次執行一個任務;if、guard和switch語句根據特定條件執行不一樣的代碼分支;以及諸如break和continue這樣的語句將執行流轉移到代碼中的另外一個點。數組

Swift還提供了一個for-in循環,能夠方便地遍歷數組、字典、範圍、字符串和其餘序列。安全

Swift的switch聲明要比許多相似c語言的版本強大得多。用例能夠匹配許多不一樣的模式,包括間隔匹配、元組和特定類型的強制類型轉換。switch案例中的匹配值能夠綁定到臨時常量或變量,以便在案例主體中使用,複雜的匹配條件能夠用where子句表示。bash

1 For-in循環

可使用for-in循環迭代序列,例如數組中的項、數字範圍或字符串中的字符。app

這個例子使用for-in循環來遍歷數組中的項:ide

let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
    print("Hello, \(name)!")
}
// Hello, Anna!
// Hello, Alex!
// Hello, Brian!
// Hello, Jack!
複製代碼

您還能夠遍歷字典來訪問它的鍵-值對。在迭代字典時,字典中的每一個項都做爲(鍵、值)元組返回,您能夠將(鍵、值)元組的成員分解爲顯式命名的常量,以便在for-in循環體中使用。在下面的代碼示例中,字典的鍵分解爲一個名爲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
複製代碼

字典的內容本質上是無序的,對它們進行迭代不能保證檢索它們的順序。特別是,向字典中插入條目的順序並不定義迭代它們的順序。有關數組和字典的更多信息,請參見集合類型oop

您還可使用具備數值範圍的for-in循環。這個示例打印一個5次表中的前幾項:post

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
複製代碼

被迭代的序列是一個從1到5(包括5)的數字範圍,如使用閉範圍操做符(…)所示。index的值設置爲範圍(1)中的第一個數字,而後執行循環中的語句。在本例中,循環只包含一條語句,它從索引的當前值的五次表中打印一個條目。在執行語句後,index的值被更新爲包含範圍(2)中的第二個值,而且再次調用print(_:separator:terminator:)函數。這個過程一直持續到範圍結束。

在上面的示例中,index是一個常量,它的值在循環的每次迭代開始時自動設置。所以,索引在使用以前不須要聲明。只需在循環聲明中包含它,就能夠隱式地聲明它,不須要let聲明關鍵字。

若是不須要序列中的每一個值,可使用下劃線代替變量名來忽略這些值。

let base = 3
let power = 10
var answer = 1
for _ in 1...power {
    answer *= base
}
print("\(base) to the power of \(power) is \(answer)")
// Prints "3 to the power of 10 is 59049"
複製代碼

上面的示例計算了一個數的幾回方(在本例中是3的10次方)。它將起始值1(即3的0次方)乘以3,10次,使用從1開始到10結束的封閉範圍。對於這種計算,每次循環中單獨的計數器值都是沒必要要的—代碼只執行正確次數的循環。用來代替循環變量的下劃線字符(_)會致使單個值被忽略,而且不會在循環的每次迭代中提供對當前值的訪問。

在某些狀況下,您可能不想使用包括兩個端點的封閉範圍。考慮在表面上畫每分鐘的刻度。您須要畫60個刻度,從0分鐘開始。使用半開範圍操做符(..<)來包含下界,而不是上界。有關範圍的更多信息,請參見範圍運算符

let minutes = 60
for tickMark in 0..<minutes {
    // render the tick mark each minute (60 times)
}
複製代碼

有些用戶可能想在他們的UI中減小標記。他們能夠選擇每5分鐘打一個分數。使用stride(from:to:by:)函數跳過不須要的標記。

let minuteInterval = 5
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
    // render the tick mark every 5 minutes (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) {
    // render the tick mark every 3 hours (3, 6, 9, 12)
}
複製代碼

2 While循環

while循環執行一組語句,直到條件變爲false爲止。當在第一次迭代開始以前不知道迭代的數量時,最好使用這種類型的循環。Swift提供了兩種while循環:

  • while 在每次遍歷循環開始時計算其條件。
  • repeat-while——在每次循環結束時計算其條件。

2.1 While

while循環從計算單個條件開始。若是條件爲真,則重複一組語句,直到條件爲假。

下面是while循環的通常形式:

while condition {
    statements
}
複製代碼

這個例子玩了一個簡單的蛇和梯zi(掘金裏面被定義爲違規內容,因此用拼音代替)的遊戲(也稱爲滑道和梯zi):

image.png
遊戲規則以下:

  • 棋盤有25個方格,目標是落在或超過第25個方格。
  • 玩家的起始方塊是「square zero」,它位於棋盤左下角。
  • 每一回合,你擲一個六面骰子,並移動該數目的方塊,遵循水平路徑指示,由上面的虛線箭頭。
  • 若是你的回合結束在梯zi的底部,你就向上爬。
  • 若是你的回合結束在一條蛇的頭部,你移動下那條蛇。

遊戲板由Int值數組表示。它的大小基於一個名爲finalSquare的常量,該常量用於初始化數組,並在本例中稍後檢查win條件。由於玩家從棋盤開始,在「square zero」上,棋盤被初始化爲26個0 Int值,而不是25。

let finalSquare = 25
var board = [Int](repeating: 0, count: finalSquare + 1)
複製代碼

一些方塊會爲蛇和梯zi設置更具體的值。有梯zi底座的方塊有一個正數可讓你在棋盤上向上移動,而蛇頭的方塊有一個負數可讓你在棋盤上向下移動。

board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
複製代碼

正方形3包含梯zi的底部,它能夠把你移動到正方形11。爲了表示這個,board[03]等於+08,這等於一個整數8(3和11之間的差)。爲了對齊值和語句,一元加號操做符(+i)顯式地與一元減號操做符(-i)一塊兒使用,小於10的數字用零填充。(這兩種文體技巧都不是絕對必要的,但它們能使代碼更整潔。)

var square = 0
var diceRoll = 0
while square < finalSquare {
    // roll the dice
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }
    // move by the rolled amount
    square += diceRoll
    if square < board.count {
        // if we're still on the board, move up or down for a snake or a ladder square += board[square] } } print("Game over!") 複製代碼

上面的示例使用了一種很是簡單的擲骰子方法。它不是生成隨機數,而是從diceRoll值0開始。每次在while循環中,diceRoll都會增長1,而後檢查它是否變得太大了。每當這個返回值等於7時,擲骰就會變得太大,並被重置爲1。結果是一個diceRoll值序列,老是一、二、三、四、五、六、一、2等等。

滾動骰子後,玩家前進的diceRoll廣場。有多是擲骰子把玩家移動到了25方,這樣遊戲就結束了。爲了處理這種狀況,代碼檢查正方形小於board數組的count屬性。若是方塊是有效的,存儲在棋盤[方塊]中的值將被添加到當前方塊值中,以使玩家在任何梯zi或蛇上上下移動。

注意
若是這個檢查沒有執行,board[square]可能會嘗試訪問一個超出board數組邊界的值,這將觸發一個運行時錯誤。

此時,當前while循環執行結束,並檢查循環的條件,以肯定是否應該再次執行該循環。若是玩家已經移動到或超過了平方數25,循環的條件將計算爲false,遊戲結束。

在這種狀況下使用while循環是合適的,由於遊戲的長度在while循環開始時並不清楚。相反,循環將一直執行到知足特定條件爲止。

2.2 Repeat-While

while循環的另外一個變體,稱爲repeat-while循環,在考慮循環條件以前,會先執行一次循環循環。 而後,它將繼續repeat-while循環,直到條件爲假。

注意
Swift中的repeat-while循環相似於其餘語言中的do-while循環。

這是repeat-while循環的通常形式:

repeat {
    statements
} while condition
複製代碼

這又是「蛇和梯zi」示例,寫爲repeat-while循環而不是while循環。 finalSquare,board,square和diceRoll的值的初始化方式與while循環徹底相同。

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
複製代碼

在此版本的遊戲中,循環中的第一個動做是檢查梯zi或蛇。 棋盤上沒有梯zi可以使玩家直奔25格,所以不可能經過爬梯zi來贏得比賽。 所以,安全地檢查蛇或梯zi是循環中的第一步。

在遊戲開始時,玩家處於「零平方」。 board [0]始終等於0,而且無效。

repeat {
    // move up or down for a snake or ladder
    square += board[square]
    // roll the dice
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }
    // move by the rolled amount
    square += diceRoll
} while square < finalSquare
print("Game over!")
複製代碼

代碼檢查完蛇和梯zi後,擲骰子,並由diceRoll方塊使玩家向前移動。 而後,當前循環執行結束。

循環的條件(square<finalSquare時)與之前相同,可是此次直到循環的第一次運行結束時才進行評估。 與以前示例中的while循環相比,repeat-while循環的結構更適合此遊戲。 在上面的repeat-while循環中,square+ = board [square]老是在循環的while條件確認正方形仍在板上後當即執行。 此行爲消除了對前面所述遊戲的while循環版本中看到的數組邊界檢查的須要。

3 條件語句

根據某些條件執行不一樣的代碼段一般頗有用。 您可能想在發生錯誤時運行額外的代碼,或者在值變得太大或過低時顯示一條消息。 爲此,您須要將部分代碼做爲條件。

Swift提供了兩種向代碼中添加條件分支的方法:if語句和switch語句。 一般,您使用if語句評估只有少數可能結果的簡單條件。 switch語句更適合於具備多個可能排列的更復雜條件,而且在模式匹配能夠幫助選擇合適的代碼分支來執行的狀況下頗有用。

3.1 If

以最簡單的形式,if語句具備單個if條件。 僅當條件爲真時,它才執行一組語句。

var temperatureInFahrenheit = 30
if temperatureInFahrenheit <= 32 {
    print("It's very cold. Consider wearing a scarf.")
}
// Prints "It's very cold. Consider wearing a scarf."
複製代碼

上面的示例檢查溫度是否小於或等於32華氏度(水的冰點)。 若是是,則會打印一條消息。 不然,不會打印任何消息,而且if語句的右大括號後代碼將繼續執行。

若是if條件爲false,則if語句能夠提供另外一組語句,稱爲else子句。 這些語句由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.")
}
// Prints "It's not that cold. Wear a t-shirt."
複製代碼

這兩個分支之一始終被執行。 因爲溫度已升高到40華氏度,所以再也不足夠寒冷以至建議戴圍巾,所以觸發了else分支。

您能夠將多個if語句連接在一塊兒以考慮其餘子句。

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.")
}
// Prints "It's really warm. Don't forget to wear sunscreen."
複製代碼

在這裏,添加了一個額外的if語句來響應特別溫暖的溫度。 最後的else子句保持不變,而且在溫度既不是過高也不是太冷的狀況下打印響應。

最後的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.")
}
複製代碼

由於溫度既不太冷也不太熱,沒法觸發if or else if條件,因此不會打印任何消息。

3.2 Switch

switch語句考慮一個值,並將其與幾種可能的匹配模式進行比較。 而後,它根據成功匹配的第一個模式執行適當的代碼塊。 switch語句提供了if語句的替代方法,用於響應多個潛在狀態。

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
}
複製代碼

每一個switch語句包含多個可能的狀況,每一個狀況都以case關鍵字開頭。 除了與特定值進行比較以外,Swift還爲每種狀況提供了幾種方法來指定更復雜的匹配模式。 這些選項將在本章後面介紹。

就像if語句的主體同樣,每種狀況都是代碼執行的單獨分支。 switch語句肯定應選擇哪一個分支。 此過程打開要考慮的值。

每一個switch語句必須詳盡。 即,所考慮的類型的每一個可能的值都必須與切換條件之一匹配。 若是不適合爲每一個可能的值都提供一個case,則能夠定義一個默認case,以涵蓋未明確尋址的全部值。 該默認狀況由default關鍵字指示,而且必須始終顯示在最後。

此示例使用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")
}
// Prints "The last letter of the alphabet"
複製代碼

switch語句的第一個字母與英文字母的第一個字母a匹配,第二個字母與最後一個字母z匹配。 由於switch必須爲每一個可能的字符而不是每一個字母字符都具備case,因此該switch語句使用默認的case來匹配除a和z以外的全部字符。 此規定確保switch語句是詳盡無遺的。

3.3 無需隱式的Fallthrough

與C和Objective-C中的switch語句相比,Swift中的switch語句不會執行到每種狀況的底部,默認狀況下不會執行下一種狀況。 相反,整個switch語句將在第一個匹配的switch狀況完成後當即完成其執行,而無需顯式的break語句。 這使得switch語句比C語言中的語句更安全和易於使用,而且避免了錯誤執行多個switch狀況。

注意
儘管在Swift中不須要break,可是您可使用break語句來匹配和忽略特定的狀況,或者在該狀況完成執行以前從匹配的狀況中中斷。 有關詳細信息,請參見Break in a Switch Statement

每一個案例的主體必須至少包含一個可執行語句。 編寫如下代碼是無效的,由於第一種狀況爲空:

let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a": // Invalid, the case has an empty body
case "A":
    print("The letter A")
default:
    print("Not the letter A")
}
// This will report a compile-time error.
複製代碼

注意
OC能夠這麼寫,不會編譯報錯。

與C中的switch語句不一樣,此switch語句不一樣時匹配「 a」和「 A」。 而是,報告一個編譯時錯誤,狀況爲「 a」:不包含任何可執行語句。 這種方法避免了從一個案例到另外一個案例的意外失敗,並使意圖更清晰,代碼更安全成爲可能。

要使用同時匹配「 a」和「 A」的單個case進行切換,請將兩個值組合爲複合case,並用逗號分隔這些值。

let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":
    print("The letter A")
default:
    print("Not the letter A")
}
// Prints "The letter A"
複製代碼

爲了便於閱讀,還能夠將多行案例寫在多行上。 有關複合案例的更多信息,請參見複合案例

注意
要在特定切換條件的末尾顯式的Fallthrough,請使用fallthrough關鍵字,如Fallthrough中所述。

3.4 間隔匹配

能夠檢查切換案例中的值是否包含在間隔中。 本示例使用數字間隔爲任意大小的數字提供天然語言計數:

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).")
// Prints "There are dozens of moons orbiting Saturn."
複製代碼

在上面的示例中,在switch語句中計算了近似計數。 每種狀況都會將該值與一個數字或間隔進行比較。 因爲近似計數的值介於12到100之間,所以爲naturalCount分配了"dozens of"值,而且將執行轉移到switch語句以外。

3.5 元組

您可使用元組在同一switch語句中測試多個值。 能夠針對不一樣的值或值的間隔測試元組的每一個元素。 或者,使用下劃線字符(_)(也稱爲通配符模式)來匹配任何可能的值。

下面的示例採用一個(x,y)點,表示爲類型爲(Int,Int)的簡單元組,並將其分類在該示例以後的圖形上。

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")
}
// Prints "(1, 1) is inside the box"
複製代碼

switch語句肯定點是在原點(0,0),在紅色x軸上,在橙色y軸上,在以原點爲中心的藍色4 x 4框內仍是在框外 。

與C不一樣,Swift容許多個switch cases判斷相同的一個或多個值。 實際上,在此示例中,點(0,0)能夠匹配全部四種狀況。 可是,若是可能有多個匹配項,則始終使用第一個匹配狀況。 點(0,0)首先匹配case(0,0),所以全部其餘匹配cases都將被忽略。

3.6 值綁定

switch case能夠命名臨時常量或變量匹配的一個或多個值,以用於case的主體中。 這種行爲稱爲值綁定,由於值綁定到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))")
}
// Prints "on the x-axis with an x value of 2"
複製代碼

switch語句肯定該點是在紅色的x軸上,在橙色的y軸上仍是在其餘位置(均不在兩個軸上)。

這三個switch case聲明瞭佔位符常量x和y,它們暫時採用來自anotherPoint的一個或兩個元組值。 第一種狀況爲case(let x,0),它匹配y值爲0的任何點,並將該點的x值分配給臨時常數x。 一樣,第二種狀況,即狀況(0,設y),匹配x值爲0的任何點,並將該點的y值分配給臨時常數y。

聲明臨時常量後,能夠在案例的代碼塊中使用它們。 在這裏,它們用於打印點的分類。

該switch語句沒有默認狀況。 最後一種狀況,即let(x,y),聲明瞭兩個能夠匹配任何值的佔位符常量的元組。 由於anotherPoint始終是兩個值的元組,因此這種狀況與全部可能的剩餘值匹配,而且不須要缺省狀況來使switch語句窮舉。

3.7 Where

switch 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")
}
// Prints "(1, -1) is on the line x == -y"
複製代碼

switch語句肯定該點是在x == y的綠色對角線上,在x == -y的紫色對角線上仍是在這兩個點上。

這三個switch case聲明瞭佔位符常量x和y,它們分別採用了yetAnotherPoint中的兩個元組值。 這些常量用做where子句的一部分,以建立動態過濾器。 僅當where子句的條件評估爲該值時,switch case才匹配point的當前值。

與前面的示例同樣,最終狀況匹配全部可能的剩餘值,所以不須要默認狀況來使switch語句窮舉。

3.8 複合Cases

共享同一主體的多個switch case可經過在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")
}
// Prints "e is a vowel"
複製代碼

switch語句的首字母大寫與英語中的全部五個小寫元音匹配。 一樣,它的第二種狀況匹配全部小寫的英語輔音。 最後,默認大小寫匹配任何其餘字符。

複合案例還能夠包括值綁定。 複合案例的全部模式都必須包含相同的值綁定集,而且每一個綁定必須從複合案例的全部模式中獲取相同類型的值。 這樣能夠確保,不管複合案例的哪一部分匹配,案例主體中的代碼始終能夠訪問綁定的值,而且該值始終具備相同的類型。

let stillAnotherPoint = (9, 0)
switch stillAnotherPoint {
case (let distance, 0), (0, let distance):
    print("On an axis, \(distance) from the origin")
default:
    print("Not on an axis")
}
// Prints "On an axis, 9 from the origin"
複製代碼

上面的狀況有兩種模式:(let distance,0)匹配x軸上的點,(0,let distance)匹配y軸上的點。 兩種模式都包含distance的綁定,而且兩種模式中的distance都是整數,這意味着案例主體中的代碼始終能夠訪問distance值。

4 控制轉移語句

經過將控制權從一個代碼段轉移到另外一個代碼段,控制轉移語句改變了代碼執行的順序。 Swift有五個控制轉移語句:

continue
break
fallthrough
return
throw
複製代碼

continue、break和fallthrough語句描述以下。return語句的描述在 Functions裏,throw語句能夠參見Propagating Errors Using Throwing Functions

4.1 Continue

Continue語句告訴循環中止正在執行的操做,並在循環的下一次迭代開始時從新開始。 它說「我完成了當前的循環迭代」,而沒有徹底離開循環。

下面的示例從小寫字符串中刪除全部元音和空格,以建立一個神祕的難題短語:

let puzzleInput = "great minds think alike"
var puzzleOutput = ""
let charactersToRemove: [Character] = ["a", "e", "i", "o", "u", " "]
for character in puzzleInput {
    if charactersToRemove.contains(character) {
        continue
    }
    puzzleOutput.append(character)
}
print(puzzleOutput)
// Prints "grtmndsthnklk"
複製代碼

上面的代碼在與元音或空格匹配時會調用continue關鍵字,從而致使循環的當前迭代當即結束並直接跳至下一個迭代的開始。

4.2 Break

break語句當即結束整個控制流語句的執行。 當您要比其餘狀況更早地終止switch或loop語句的執行時,能夠在switch或loop語句內使用break語句。

4.2.1 循環語句裏的Break

在循環語句中使用break時,會當即結束循環的執行,並在循環的右括號(})以後將控制權轉移給代碼。 不會執行循環的當前迭代中的其餘代碼,也不會啓動循環的其餘迭代。

4.2.2 Switch語句裏的Break

在switch語句內使用break時,會致使switch語句當即結束其執行,並將控制權轉移到switch語句的右括號(})以後。

此行爲可用於匹配和忽略switch語句中的一個或多個狀況。 因爲Swift的switch語句是詳盡無遺的,而且不容許空白的案例,所以有時有必要故意匹配並忽略案例,以使意圖明確。 您能夠經過將break語句做爲要忽略的案例的總體來編寫。 當該案例與switch語句匹配時,案例內的break語句將當即終止switch語句的執行。

注意
僅包含註釋的switch case將報告爲編譯時錯誤。 註釋不是語句,不會致使switch case被忽略。 始終使用break語句忽略switch case。

下面的示例打開一個Character值,並肯定它是否表明四種語言之一的數字符號。 爲簡便起見,在一個switch case中包含多個值。

let numberSymbol: Character = "三"  // Chinese symbol for the number 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).")
}
// Prints "The integer value of 三 is 3."
複製代碼

本示例檢查numberSymbol以肯定數字1到4是拉丁,阿拉伯,中文仍是泰文符號。若是找到匹配項,則switch語句的一種狀況將設置可選的Int? 變量,可能爲一個可選的整數值。

在switch語句完成執行以後,該示例使用可選綁定來肯定是否找到了值。 PossibleIntegerValue變量是可選類型,所以具備隱式初始值nil,所以,只有在switch語句的前四種狀況之一將可能性IntegerValue設置爲實際值時,可選綁定纔會成功。

因爲在上面的示例中列出全部可能的Character值不切實際,所以默認狀況下會處理全部不匹配的字符。 此默認狀況不須要執行任何操做,所以它以單個break語句爲主體編寫。 匹配默認大小寫後,break語句將結束switch語句的執行,而且代碼將從if let語句繼續執行。

4.3 Fallthrough

在Swift中,switch語句不會落入每種狀況的底部,也不會落入下一種狀況。 即,第一個匹配的狀況一旦完成,整個switch語句就完成其執行。 相比之下,C要求您在每一個switch狀況的末尾插入一個顯式的break語句,以防止失敗。 避免默認的失敗,意味着Swift的switch語句比C語言中的對應語句更加簡潔和可預測,所以避免了錯誤地執行多個switch狀況。

若是您須要C風格的fallthrough行爲,則可使用fallthrough關鍵字視狀況選擇加入此行爲。 下面的示例使用fallthrough來建立數字的文本描述。

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)
// Prints "The number 5 is a prime number, and also an integer."
複製代碼

本示例聲明一個名爲description的新String變量,併爲其分配一個初始值。 而後,該函數使用switch語句判斷integerToDescribe的值。 若是integerToDescribe的值是列表中的質數之一,則該函數會將文本追加到描述末尾,以指出該數字是質數。 而後,它也使用fallthrough關鍵字來「落入」默認大小寫。 默認狀況下,在描述末尾添加了一些額外的文本,而且switch語句已完成。

除非integerToDescribe的值在已知質數列表中,不然第一個switch case根本不匹配。 由於沒有其餘特定狀況,因此integerToDescribe與默認狀況匹配。 在switch語句執行完以後,使用print(_:separator:terminator :)函數打印號碼的描述。 在此示例中,數字5被正確標識爲質數。

注意
fallthrough關鍵字不檢查致使執行陷入的switch case的條件。 fallthrough關鍵字僅致使代碼執行直接移至下一個case(或默認case)塊內的語句,就像C的標準switch語句行爲同樣。

4.4 Labeled語句

在Swift中,您能夠將循環和條件語句嵌套在其餘循環和條件語句中,以建立複雜的控制流結構。 可是,循環和條件語句均可以使用break語句提早結束執行。 所以,有時對於明確要讓break語句終止的循環或條件語句頗有用。 一樣,若是您有多個嵌套循環,則明確說明continue語句應該影響哪一個循環會頗有用。

爲了實現這些目標,可使用語句label標記循環語句或條件語句。 對於條件語句,能夠將語句label與break語句一塊兒使用以結束帶label的語句的執行。 對於循環語句,能夠將語句label與break或continue語句一塊兒使用,以結束或繼續執行帶label的語句。

帶label的語句是經過在與該語句的介紹者關鍵字相同的行上放置一個標籤來表示的,後跟一個冒號。 這是一個while循環的語法示例,儘管原理對於全部循環和switch語句都是相同的:

label name: while condition {
    statements
}
複製代碼

下面的示例使用帶有label的while循環的break和continue語句,爲您在本章前面介紹的Snakes and Ladders遊戲的改編版提供幫助。 此次,遊戲有一個額外的規則:

  • 爲了贏,您必須準確落在25號square上。

若是特定骰子擲骰使您超過25號方格,則必須再次擲骰,直到擲出準確的數字才能落在25號方格上。

遊戲板與之前相同。

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循環和switch語句來實現遊戲的邏輯。 while循環具備一個稱爲gameLoop的語句標籤,以指示它是Snakes and Ladders遊戲的主要遊戲循環。

while循環的條件是while方格== finalSquare,以反映您必須準確落在方格25上。

gameLoop: while square != finalSquare {
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }
    switch square + diceRoll {
    case finalSquare:
        // diceRoll will move us to the final square, so the game is over
        break gameLoop
    case let newSquare where newSquare > finalSquare:
        // diceRoll will move us beyond the final square, so roll again
        continue gameLoop
    default:
        // this is a valid move, so find out its effect
        square += diceRoll
        square += board[square]
    }
}
print("Game over!")
複製代碼

在每一個循環開始時擲骰子。 循環不是當即移動播放器,而是使用switch語句來考慮移動結果並肯定是否容許移動:

  • 若是擲骰子將玩家移至最後一個方塊,則遊戲結束。 break gameLoop語句將控制權轉移到while循環以外的第一行代碼,從而結束了遊戲。
  • 若是擲骰子會將玩家移出最終方塊,則該移動無效,玩家須要再次擲骰子。 Continue gameLoop語句結束當前的while循環迭代並開始循環的下一個迭代。 在全部其餘狀況下,擲骰子都是有效的舉動。
  • 玩家以diceRoll方塊向前移動,遊戲邏輯檢查是否有蛇和梯zi。 而後循環結束,控制返回while條件,以決定是否須要再轉一圈。

注意
若是上面的break語句不使用gameLoop標籤,它將脫離switch語句,而不是while語句。 使用gameLoop標籤能夠清楚地代表應該終止哪一個控制語句。

調用continue gameLoop跳轉到循環的下一個迭代時,不必定必須使用gameLoop標籤。 遊戲中只有一個循環,所以,繼續聲明會影響哪一個循環沒有任何歧義。 可是,將gameLoop標籤與continue語句一塊兒使用不會有任何危害。 這樣作與標籤在break語句旁邊的用法一致,並有助於使遊戲的邏輯更易於閱讀和理解。

5 提早退出(guard)

與if語句同樣,guard語句根據表達式的布爾值執行語句。 您使用保護語句要求條件必須爲true,以便執行保護語句以後的代碼。 與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"])
// Prints "Hello John!"
// Prints "I hope the weather is nice near you."
greet(person: ["name": "Jane", "location": "Cupertino"])
// Prints "Hello Jane!"
// Prints "I hope the weather is nice in Cupertino."
複製代碼

若是知足了guard語句的條件,則在guard語句的右大括號以後代碼將繼續執行。 使用條件綁定(做爲條件的一部分)爲值分配了值的任何變量或常量,對於該guard語句所在的其他代碼塊都可用。

若是不知足該條件,則執行else分支內的代碼。 該分支必須轉移控制權以退出其中出現guard語句的代碼塊。 它能夠經過控制傳遞語句(例如return,break,continue或throw)來執行此操做,也能夠調用不返回的函數或方法,例如fatalError(_:file:line :)。

與使用if語句執行相同的檢查相比,使用guard語句知足要求能夠提升代碼的可讀性。 它使您能夠編寫一般執行的代碼,而無需將其包裝在else塊中,並使處理違反要求的代碼保持在要求旁邊。

6 檢查API可用性

Swift具備檢查API可用性的內置支持,可確保您不會意外使用給定部署目標上不可用的API。

編譯器使用SDK中的可用性信息來驗證代碼中使用的全部API在項目指定的部署目標上是否可用。 若是您嘗試使用不可用的API,Swift會在編譯時報告錯誤。

您能夠在if或guard語句中使用可用性條件,以有條件地執行代碼塊,具體取決於要使用的API在運行時是否可用。 當編譯器驗證該代碼塊中的API可用時,將使用可用性條件中的信息。

if #available(iOS 10, macOS 10.12, *) {
    // Use iOS 10 APIs on iOS, and use macOS 10.12 APIs on macOS
} else {
    // Fall back to earlier iOS and macOS APIs
}
複製代碼

上面的可用性條件指定在iOS中,if語句的主體僅在iOS 10及更高版本中執行; 在macOS中,僅在macOS 10.12及更高版本中。 最後一個參數*是必需的,它指定在任何其餘平臺上,if的主體在目標所指定的最小部署目標上執行。

在通常狀況下,可用性條件採用平臺名稱和版本的列表。 您使用平臺名稱,例如iOS,macOS,watchOS和tvOS,有關完整列表,請參閱Declaration Attributes。 除了指定主要版本號(例如iOS 8或macOS 10.10)以外,您還能夠指定次要版本號(例如iOS 11.2.6和macOS 10.13.3)。

if #available(platform name version, ..., *) {
    statements to execute if the APIs are available
} else {
    fallback statements to execute if the APIs are unavailable
}
複製代碼

總結

這一章節學習的內容在這裏作個總結:

  • for-in循環:能夠直接聲明變量來做爲數組循環的返回值,或使用元組來做爲字典循環的返回值,或者使用通配符_來忽略值;還可使用...和..<來進行範圍循環;還可使用stride(from:to:by:)函數老跳過不須要的標記循環。
  • while循環:有兩種while和repeat-while(相似C語言中的do-while)循環。
  • if條件語句,這個你們應該都會,這裏不作贅述。
  • switch語句,swift裏的和OC裏的不同,switch判斷的值能夠是任何類型,不像OC只能匹配整型,在swift裏在一個case末尾不須要加break來跳出switch case,默認會直接結束switch case的判斷;switch能夠用間隔匹配的功能,如:case 1..<5:來匹配一個範圍的值;還可使用元組,如:case (0, 0)、case(_, 0)、case(0, _)、case(-2...2, -2...2)來匹配多個值;還可使用值綁定,如:case (let x, 0)、case(0, let y)、case(let x, let y)來匹配一個或多個值;還可使用where關鍵字來添加條件判斷,如:case let (x, y) where x== y;還能夠在case後加入多個模式,如:case "a","e","i"
  • 控制轉移語句:continue、break,swift裏的和OC的用法差很少;swift多出的功能是fallthrough和帶標籤語句。使用fallthrough來使switch語句能繼續執行下一個case內的內容;使用帶標籤的語句,配合switch語句和break、contine判斷帶標籤的變量是否知足條件,具體的實現能夠看上面的代碼。
  • guard語句,功能和if語句同樣,不過用guard語句條件判斷會更高效,且代碼更簡潔。
  • 檢查API可用性,可配合if和guard語句使用,如:if #available(platform name version, ..., *)

上一章節:集合類型

下一章節:函數

參考文檔:Swift - Control Flow

相關文章
相關標籤/搜索