天天看點

Swift程式設計六(控制流)控制流

案例代碼下載下傳

控制流

Swift提供了各種控制流程語句。這些包括多次執行任務的while循環; if,guard和switch基于特定條件執行不同代碼分支的語句; 和如break和continue對執行流在代碼中轉移到另一個點的語句。

Swift還提供了for- in循環,可以很容易地周遊數組,字典,範圍,字元串和其它序列。

Swift的switch聲明比許多類C語言中的聲明強大得多。案例可以比對許多不同的模式,包括區間比對,元組和特定類型的強制轉換。switch案例中的比對值可以綁定到臨時常量或變量以在案例主體中使用,複雜比對條件可以為每個案例添加用where子句表示。

for-in循環

使用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循環主體内使用。在下面的代碼示例中,字典的鍵被分解為一個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
*/
           

Dictionary的内容本質上是無序的,并且疊代它們并不能保證它們的檢索順序。特别是,将項目插入到Dictionary中的順序不會定義它們被疊代的順序。有關數組和字典的更多資訊,請參閱集合類型。

還可以使用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
*/
           

正如通過使用閉區間運算符(…)所訓示的那樣,疊代的序列是從1到的數字5範圍。index的值設定為範圍中的第一個數字(1),然後執行循環内的語句。在這種情況下,循環隻包含一個語句,該語句從五次表中輸出目前條目index的值。執行語句後,index更新值以包含範圍中的第二個值(2),并再次調用print(_:separator:terminator:)函數。此過程一直持續到達範圍結束。

在上面的示例中,index是一個常量,其值在循環的每次疊代開始時自動設定。是以,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)")
           

上面的例子計算一個數字與另一個數字的幂(在這種情況下,計算3的10次方)。它使用以1開頭和10結尾的閉合範圍将起始值1(即,與其3幂0)相乘3。對于此計算,每次循環時各個計數值都是不必要的 - 代碼隻是執行循環正确的次數。用于代替循環變量的下劃線字元(_)會導緻單個值被忽略,并且在循環的每次疊代期間不提供對目前值的通路。

在某些情況下,可能不希望使用包含兩個端點的閉合範圍。考慮在表盤上繪制每分鐘的刻度線。想繪制60刻度線,從0分鐘開始。使用半開範圍運算符(…<)包括下限但不包括上限。有關範圍的更多資訊,請參閱範圍運算符。

let minutes = 60
for tickMark in 0..<minutes {
    // 每分鐘标記一次(60次)
}
           

某些使用者可能希望在其UI中使用更少的刻度線。他們可能更喜歡每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

一個while循環通過計算一個條件開始。如果條件是true,則重複一組語句,直到條件變為false。

這是while循環的一般形式:

while condition {
    statements
}
           

這個例子玩一個簡單的蛇和梯子遊戲(也稱為滑道和梯子):

Swift程式設計六(控制流)控制流

遊戲規則如下:

  • 該闆有25個正方形,目标是在25号方格或更遠的地方着陸。
  • 玩家的起始方格是“方形零”,它位于棋盤的左下角。
  • 每滾動一個六面骰子,按照上面虛線箭頭所示的水準路徑移動那個數量的方塊。
  • 如果終點在梯子的底部,向上移動那個梯子頂部。
  • 如果終點在蛇的頭部,就會向下移動那條蛇的尾部。

遊戲闆由一系列Int值表示。它的大小基于一個被叫做finalSquare的常量,用于初始化數組,并在示例後面檢查獲勝條件。因為玩家從“方形零”開始,是以面闆初始化為26個Int值0,而不是25個。

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
           

方格3包含梯子底座要移動到方格11.要表示這樣的階梯的底部,board[03]等于+08,這相當于一個整數值8(3和11之間的差)。為了對齊值和語句,一進制加運算符(+i)明确地與一進制減運算符(-i)一起使用,并且低于10的數字用零填充。(技術風格都不是必需的,但它們會導緻更簡潔的代碼。)

var square = 0
var diceRoll = 0
while square < finalSquare {
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }
    square += diceRoll
    if square < board.count {
        square += board[square]
    }
}
print("Game over!")
           

上面的例子使用一種非常簡單的方法來進行骰子滾動。它不是生成随機數,而是以diceRoll值為0開頭。每次while循環時,diceRoll都會增加1,然後檢查它是否變得太大。每當此傳回值等于時7,骰子滾動變得太大并重置為值1。diceRoll的結果是一個序列,值始終是1,2,3,4,5,6,1,2等。

擲骰子後,玩家向前移動diceRoll。擲骰子可能已經将玩家移動到25以上,在這種情況下遊戲結束了。為了應對這種情況,代碼檢查square釋放小于board數組的count屬性。如果square有效,則将存儲的值board[square]添加到目前square值,以使玩家向上或向下移動。

注意

如果未執行此檢查,board[square]可能會嘗試通路board數組邊界之外的值,這将觸發運作時錯誤。

然後,目前while循環執行結束,并檢查循環的條件以檢視是否應該再次執行循環。如果玩家已經移動到或超過方格25,則循環的條件評估為false并且遊戲結束。

在這種情況下while循環是适當的,因為遊戲的長度在開始不是明确的。相反,執行循環直到滿足特定條件。

Repeat-While

while循環的另一個變體(稱為repeat-while循環)在考慮循環條件之前首先執行單個循環通過循環塊。然後它繼續重複循環,直到條件為止false。

注意

Swift的repeat-while循環類似于其他語言的do-while循環。

這裡有一個的一般形式repeat- while循環:

repeat {
    statements
} while condition
           

這裡再次是蛇和梯子示例,寫為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
           

在這個版本的遊戲中,循環中的第一個動作是檢查梯子或蛇。棋盤上沒有任何梯子将玩家直接拉到25方,是以不可能通過向上移動梯子來赢得比賽。是以,檢查蛇或梯子作為循環中的第一個動作是安全的。

在遊戲開始時,玩家處于“方形零”。board[0]總是等于0并且沒有效果。

條件語句

基于某些條件執行不同的代碼段通常很有用。可能希望在發生錯誤時運作額外的代碼,或者在值變得太高或太低時顯示消息。為此,可以使代碼的一部分作為條件。

Swift提供了兩種方法來為代碼添加條件分支:if語句和switch語句。通常,使用該if語句來評估隻有少數可能結果的簡單條件。switch語句更适合具有多種可能排列的更複雜條件,并且模式比對在幫助選擇要執行的适當代碼分支的情況下非常有用。

If

在最簡單的形式中,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.
*/
           

上面的例子檢查溫度是否小于或等于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.")
}

/*
列印結果:

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.")
}

/*
列印結果:

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或else if條件,是以不會列印資訊。

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語句的主體一樣,每個語句case都是代碼執行的獨立分支。switch語句确定應選擇哪個分支。此過程稱為切換正在考慮的值。

每個switch聲明都必須詳盡無遺。也就是說,所考慮類型的每個可能值必須與其中一個switch案例相比對。如果為每個可能的值提供案例是不合适的,則可以定義預設案例以涵蓋未明确解決的任何值。此預設情況由default關鍵字訓示,并且必須始終顯示在最後。

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
*/
           

switch陳述的第一個案例與英文字母的第一個字母a相比對,其第二個案例與最後一個字母z相比對。因為switch必須具有每個可能字元,而不僅僅是每個字母字元,是以此switch語句使用default來比對除a和z之外的所有字元。該條款確定switch聲明是詳盡無遺的。

沒有隐含的比對後面情況

switchC和Objective-C中的switch語句相比,Swift中的語句不會在每個案例的底部落入預設情況下的下一個。相反,switch隻要第一個比對的switch情況完成,整個語句就會完成執行,而不需要顯式break語句。這使得switch語句比C中的語句更安全,更容易使用,并且避免switch錯誤地執行多個案例。

注意

盡管break在Swift中不需要,但可以使用break語句來比對和忽略特定情況,或者在該情況完成執行之前中斷比對的情況。有關詳細資訊,請參閱Switch語句中的Break。

每個案例的主體必須包含至少一個可執行語句。編寫以下代碼無效,因為第一種情況是空的:

let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a": 
case "A":
    print("The letter A")
default:
    print("Not the letter A")
}
           

不像C的switch語句,該switch語句不會比對"a"和"A"。相反,它報告一個case “a”:不包含任何可執行語句的編譯時錯誤。這種方法避免了從一個案例到另一個案例的意外洩漏,并使得代碼更安全更加清晰。

為了使switch與比對"a"和"A"二者的之一的情況,這兩個值組合成的情況下,用逗号分隔的值。

let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":
    print("The letter A")
default:
    print("Not the letter A")
}

/*
列印結果:

The letter A
*/
           

為了便于閱讀,複合案例也可以通過多行編寫。有關複合案例的更多資訊,請參閱複合案例。

注意

要在特定switch情況的最後明确地落空,請使用fallthrough關鍵字,如Fallthrough中所述。

區間比對

switch可以檢查案例中的值是否包含在間隔中。此示例使用數字間隔為任何大小的數字提供自然```計數:

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都将該值與數字或間隔進行比較。因為approximateCount值介于12和100之間,是以naturalCount會賦"dozens of"值,并且執行将從switch語句中轉移出來。

元組

可以使用元組在同一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")
}

/*
列印結果:

(1, 1) is inside the box
*/
           
Swift程式設計六(控制流)控制流

該switch語句确定該點是在原點(0,0),紅色x軸,橙色y軸上,在原點中心的藍色4×4框内,還是在框的外部。

與C不同,Swift允許多個switch案例考慮相同的值或值域。實際上,點(0,0)可以比對此示例中的所有四種情況。但是,如果可以進行多次比對,則始終使用第一個比對寫。點(0,0)将首先比對case (0, 0),是以将忽略所有其他比對的情況。

值綁定

一個switch情況下,可以将其命名為臨時常量或變量,情況下使用相比對的一個或多個值。此行為稱為值綁定,因為值綁定到案例正文中的臨時常量或變量。

下面的示例采用(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
*/
           
Swift程式設計六(控制流)控制流

該switch語句确定該點是在紅色x軸上,橙色y軸上還是其他位置)。

這三種switch情況下,聲明占位符常量x和y,暫時采取在一個或兩個元組值的anotherPoint。第一種情況case (let x, 0),比對任何y具有值0的點,并将該點的x值配置設定給臨時常量x。類似地,第二種情況case (0, let y)比對x具有值0的任何點,并将該點的y值配置設定給臨時常量y。

聲明臨時常量後,可以在案例的代碼塊中使用它們。在這裡,它們用于列印點的分類。

本switch聲明沒有default案例。最後一種情況case let (x, y),聲明了一個可以比對任何值的兩個占位符常量的元組。因為anotherPoint總是兩個值的元組,是以這個案例比對所有可能的剩餘值,并且不需要一個default案例來使switch語句詳盡無遺。

Where

一個switch情況下可以使用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
*/
           
Swift程式設計六(控制流)控制流

該switch語句确定該點是否在綠色對角線x == y上,在紫色對角線x == -y上,或者兩者都沒有。

這三種switch情況下,聲明占位符常量x和y,暫時采取從yetAnotherPoint的兩元組值。這些常量用作where子句的一部分,以建立動态過濾器。僅當switch語句的where子句的條件為true時,該案例才比對point目前值。

與前面的示例一樣,最終比對所有可能的剩餘值,是以不需要一個default案例來使switch語句詳盡無遺。

複合案例

共享相同主體的多個Switch案例可以通過在每個模式之間用逗号分隔寫幾個模式來組合。如果任何模式比對,則認為該情況比對。如果清單很長,則可以在多行上寫入模式。例如:

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聲明的第一個案例與英語中的所有五個小寫元音相比對。同樣,它的第二個案例比對所有小寫英語輔音。最後,default案例與任何其他角色比對。

複合案例還可以包括值綁定。複合案例的所有模式都必須包含同一組值綁定,并且每個綁定必須從複合案例中的所有模式中擷取相同類型的值。這確定了,無論複合案例的哪個部分比對,案例正文中的代碼總是可以通路綁定的值,并且值始終具有相同的類型。

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")
}

/*
列印結果:

On an axis, 9 from the origin
*/
           

在上面有兩個模式:在x軸的比對點(let distance, 0)和在y軸的比對點(0, let distance)。這兩種模式都包含一個綁定distanced,并且在兩種模式中都是一個整數 - 這意味着該主體中的代碼總是可以通路一個distance的值。

控制轉移語句

控制轉移語句通過将控制從一段代碼轉移到另一段代碼來改變代碼執行的順序。Swift有五個控制轉移語句:

  • continue
  • break
  • fallthrough
  • return
  • throw

continue,break和fallthrough語句描述如下。函數中描述了return語句,并且在使用Throwing函數傳播錯誤中描述了throw語句。

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)

/*
列印結果:

grtmndsthnklk
*/
           

上面的代碼在比對元音或空格時調用continue關鍵字,導緻循環的目前疊代立即結束并直接跳轉到下一次疊代的開始。

Break

break語句立即結束整個控制流語句的執行。當想要比其他情況更早地終止語句的執行時,break可以在一個switch或循環語句中使用。

break循環

在循環語句中使用break時,立即結束循環的執行,并在循環的右括号(})之後将控制權轉移給代碼。不執行來自目前循環疊代的進一步代碼,并且不再開始循環的疊代。

break Switch語句

在switch語句中使用break時,會使switch語句立即結束其執行,并在switch語句的右括号(})後将控制權轉移給代碼。

此行為可用于比對和忽略switch語句中的一個或多個案例。由于Swift的switch陳述是詳盡的,并且不允許空案件,是以有時需要故意比對并忽略案例,以使意圖明确。可以通過将break語句寫為要忽略的案例的整個主體來完成此操作。當該情況與switch語句比對時,案例内的break語句立即結束switch語句的執行。

注意

一個switch僅包含comment的情況下被報告為編譯時錯誤。comment不是陳述,也不會導緻switch案例被忽略。始終使用break語句來忽略switch案例。

以下示例打開一個Character值,并确定它是否表示四種語言之一的數字元号。為簡潔起見,單個switch案例涵蓋了多個值。

let numberSymbol: Character = "三"
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設定為适當的整數值。

在switch的語句執行完成後,該示例使用可選綁定來确定值是否被發現。由于是一個可選類型,possibleIntegerValue變量具有隐式初始值nil,是以隻有possibleIntegerValue在switch語句的前四種情況之一被設定為實際值時,可選綁定才會成功。

因為在上面的示例中列出每個可能的Character值是不切實際的,是以一個default案例處理任何不比對的字元。這種default情況不需要執行任何操作,是以它使用單個break語句作為其主體。隻要default案例比對,break語句就會結束switch語句的執行,并從if let語句繼續執行代碼。

Fallthrough

在Swift中,switch語句不會落入每個案例的底部并進入下一個案例。也就是說,switch一旦第一個比對的案例完成,整個語句就完成了它的執行。相反,C要求break在每個switch案例的末尾插入一個明确的語句,以防止通過。避免預設的下降意味着Swift switch語句比C中的對應語句更簡潔和可預測,是以它們避免switch錯誤地執行多個案例。

如果需要C樣式的直通行為,則可以使用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)

/*
列印結果:

The number 5 is a prime number, and also an integer.
*/
           

此示例聲明一個名為description的新String變量,并為其配置設定一個初始值。然後使用switch語句考慮integerToDescribe的值。如果integerToDescribe值是清單中的素數之一,則将文本附加到description末尾,以訓示該數字是素數。然後它使用fallthrough關鍵字“落入” default案例。default案例在描述的末尾添加了一些額外的文本,并且switch語句已完成。

除非integerToDescribe值在已知素數清單中,否則它與第一種switch情況完全不比對。因為沒有其他特定情況,是以integerToDescribe比對default情況。

在之後switch的語句執行完畢,數的描述是使用print(_:separator:terminator:)函數列印。在此示例中,數字5被正确識别為素數。

注意

fallthrough關鍵字不檢查使switch執行陷入此情況的條件。fallthrough關鍵字簡單地使代碼執行直接移動到下一個的情況下(或内的default語句的情況下)嵌段,如C的标準switch語句的行為。

标簽語句

在Swift中,可以在其他循環和條件語句中嵌套循環和條件語句,以建立複雜的控制流結構。但是,循環和條件語句都可以使用break語句過早地結束執行。是以,有時候明确要求break語句終止的循環或條件語句是有用的。類似地,如果有多個嵌套循環,那麼明确該continue語句應該影響哪個循環可能很有用。

要實作這些目标,可以使用語句标簽标記循環語句或條件語句。使用條件語句,可以使用帶标簽語句的break語句來結束帶标簽語句的執行。使用循環語句,可以使用帶有标簽語句的break、continue語句來結束或繼續執行帶标簽的語句。

标簽語句通過在語句相同行上關鍵字前放置标簽名來訓示,後跟冒号。這是while循環的這種文法的一個例子,然而所有循環和switch語句的原理是相同的:

label name: while condition {
    statements
}
           

下面的示例使用帶有标簽循環的break和continue語句,以獲得本章前面while所述的蛇和梯子遊戲的改編版本。這一次,遊戲有一個額外的規則:

要赢,你必須準确降落在25号方格上。

如果一個特定的骰子卷超過25号方格,必須再次滾動,直到你滾動所需的确切數字落在方形25上。

遊戲闆和以前一樣。

Swift程式設計六(控制流)控制流

值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,以表明它是蛇和梯子遊戲主要的遊戲循環。

該while循環的條件為square != finalSquare,以反映您必須準确降落在廣場25。

gameLoop: while square != finalSquare {
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }
    switch square + diceRoll {
    case finalSquare:
        break gameLoop
    case let newSquare where newSquare > finalSquare:
        continue gameLoop
    default:
        square += diceRoll
        square += board[square]
    }
}
print("Game over!")

/*
列印結果:

Game over!
*/
           

骰子在每個循環開始時滾動。循環使用switch語句來考慮移動的結果并确定是否允許移動,而不是立即移動:

  • 如果骰子将玩家移動到最後的方格,則遊戲結束。break gameLoop語句将控制轉移到while循環外的第一行代碼,結束遊戲。
  • 如果骰子将移動玩家超越最後的方格,此舉是無效的,玩家需要再次滾動。continue gameLoop語句結束目前循環while疊代并開始循環的下一次疊代。
  • 在所有其他情況下,擲骰子是一個有效的舉動。玩家通過diceRoll向前移動,遊戲邏輯檢查任何蛇和梯子。然後循環結束,控制傳回到while條件。

注意

如果上面的break語句沒有使用gameLoop标簽,将結束switch語句,而不是while語句。使用gameLoop标簽可以清楚地終止哪個控制語句。

在調用continue gameLoop跳轉到循環的下一次疊代時,不一定要使用gameLoop标簽。遊戲中隻有一個循環,是以對于continue語句将影響哪個循環沒有歧義。但是,在語句中使用gameLoop标簽沒有任何害處。這樣做帶有标簽的continue與break語句一起使用,有助于使遊戲的邏輯更清晰,易讀和了解。

提前退出

guard語句,像if語句,執行以布爾值表達式為條件的語句。使用guard語句要求條件必須為true才能執行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"])
greet(person: ["name": "Jane", "location": "Cupertino"])

/*
列印結果:

Hello John!
I hope the weather is nice near you.
Hello Jane!
I hope the weather is nice in Cupertino.
*/
           

如果滿足guard語句的條件,則guard語句的右括号後繼續執行代碼。使用可選綁定作為條件的一部分配置設定值的任何變量或常量都可用于guard語句的其餘代碼塊。

如果不滿足該條件,則執行else分支内的代碼。該分支必須轉移控制以退出guard語句出現的代碼塊。可以做到轉移控制權這一點的語句,如return,break,continue,或者throw,也可以調用一個函數或方法不傳回,如fatalError(_:file:line:)。

guard相比if語句進行相同的檢查,使用guard語句可以提高代碼的可讀性。它允許編寫通常執行的代碼而不将其包裝在else塊中,并且它允許保留處理違反條件的代碼。

檢查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-對于完整清單,請參閱聲明屬性。除了指定主要版本号(如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
}