Swift提供多種控制流語句。
其中包括多次執(zhí)行任務(wù)的while循環(huán);
if、guard和switch語句根據(jù)特定條件執(zhí)行不同的代碼分支;語句break和continue將執(zhí)行流轉(zhuǎn)移到代碼中的另一個點。
Swift還提供了一個for-in循環(huán),可以輕松地遍歷array、dictionary、range、string和其他序列。
Swift的switch聲明比它在許多c語言中的對應(yīng)聲明要強大得多。用例可以匹配許多不同的模式,包括間隔匹配、元組和對特定類型的強制轉(zhuǎn)換。
switch 用例中的匹配值可以綁定到臨時常量或變量,以便在用例主體中使用,并且可以用where子句為每種用例表示復雜的匹配條件。
For-In Loops
您可以使用for-in循環(huán)遍歷序列,例如數(shù)組中的項、數(shù)字范圍或字符串中的字符。
例如:遍歷數(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
// cats have 4 legs
// spiders have 8 legs
字典的內(nèi)容本質(zhì)上是無序的,遍歷它們并不能保證檢索它們的順序。特別是,將項插入字典的順序并不定義它們迭代的順序。
例如: 遍歷數(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
如果不需要序列中的每個值,可以使用下劃線代替變量名來忽略這些值。
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"
在某些情況下,您可能不希望使用封閉區(qū)間(包括兩個端點)。考慮在手表表盤上畫上每分鐘的刻度。你想要畫60個勾,從0分鐘開始。使用半開區(qū)間運算符(..<)來包含下界,而不包含上界。
let minutes = 60
for tickMark in 0..<minutes {
// render the tick mark each minute (60 times)
}
間斷執(zhí)行: 半開區(qū)間-右開
let minuteInterval = 5
for tickMark in stride(from: 0, to: 60, by: minuteInterval) {
print(tickMark)
}
輸出結(jié)果:
0
5
10
15
20
25
30
35
40
45
50
55
數(shù)值閉區(qū)間還可以這樣表示:
let hours = 12
let hourInterval = 3
for tickMark in stride(from: 3, through: hours, by: hourInterval) {
print(tickMark)
}
輸出結(jié)果
3
6
9
12
While Loops while 循環(huán)
while循環(huán)執(zhí)行一組語句,直到條件變?yōu)閒alse。當在第一次迭代開始之前迭代的次數(shù)未知時,最好使用這種循環(huán)。Swift提供了兩種while循環(huán):
- while :在每次循環(huán)開始時計算其條件。
- repeat-while :每次循環(huán)結(jié)束時計算其條件。
while
while循環(huán)先判斷條件語句開始。如果為true,則執(zhí)行一次語句,然后繼續(xù)判斷條件語句,直到條件變?yōu)閒alse停止執(zhí)行。
while condition {
statements
}
簡單例子
var n = 1
while n<10 {
print(n)
n += 5
}
輸出結(jié)果
1
6
顯然執(zhí)行了 2次,第一次判斷 n=1 時條件為true,第二次判斷 n = 6 時條件為true,第三次判斷 n = 11 條件為false就不執(zhí)行了
repeat-while
先執(zhí)行一次代碼塊中的語句,然后判斷條件,條件成立true,則再次執(zhí)行代碼塊中的語句,直到條件不成立false時,就不再執(zhí)行代碼塊中的語句
repeat {
statements
} while condition
同樣來個簡單例子
var m = 1
repeat {
print(m)
m += 5
} while m < 10
輸出結(jié)果:
1
6
執(zhí)行代碼時m=1,第一次執(zhí)行循環(huán)體后 m 變?yōu)榱?,判斷條件成立 true,第二次執(zhí)行循環(huán)體后 m 變?yōu)榱?11,判斷條件不成立 false 不再執(zhí)行循環(huán)體中代碼
Conditional Statements 條件語句
Swift提供了兩種向代碼添加條件分支的方法:if語句和switch語句。通常,您使用if語句來評估只有少數(shù)可能結(jié)果的簡單條件。switch語句更適合具有多種可能排列的更復雜的條件,在模式匹配可以幫助選擇要執(zhí)行的適當代碼分支的情況下非常有用。
if
單個 if 語句
let n = 5
if n < 10 {
print(n)
}
輸出 5
if else 語句簡單例子
var age = 16
if age < 18 {
print("未成年人,禁止進入網(wǎng)吧!")
} else {
print("成年人,請付費上網(wǎng)!")
}
多重 if 語句簡單例子:考試成績等級
let score = 80
if score < 60 {
print("考試成績:不及格")
} else if score > 80 {
print("考試成績: 優(yōu)秀")
} else {
print("考試成績: 良好")
}
嵌套 if
if score < 60 {
print("考試成績:不及格")
} else {
if score < 80 {
print("考試成績: 良好")
} else {
print("考試成績: 優(yōu)秀")
}
}
當然可以多重嵌套,和更多的多重if,此時推薦使用 switch。
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
}
如果case中包含了所有可能性,那么就不用default了。
當多種情況,執(zhí)行相同操作時,在 case 中使用 逗號 分隔。
同樣寫個簡單例子,考試成績等級
let score = 80
let l1 = 80...100
let l2 = 60..<80
let l3 = 0..<60
switch score {
case l1:
print("考試成績優(yōu)秀")
case l2:
print("考試成績良好")
case l3:
print("考試成績不及格")
default:
print("缺考")
}
//打印: 考試成績優(yōu)秀
上面成績?yōu)?80 分,在l1等級。
注意:swift中 switch語句中不需要使用 break,使用也不會報錯。其他語言中都有使用break,以防止進入下一個case。swift 中并不會進入下一個case,但是每個case中至少有一條語句。
Tuples 元組匹配
您可以使用元組在同一個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"
Value Bindings 值綁定
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))")
}
// Prints "on the x-axis with an x value of 2"
switch語句確定該點是在紅色的x軸上、橙色的y軸上,還是在其他地方(兩個軸都沒有)。
這三個開關(guān)用例聲明占位符常量x和y,它們臨時接受來自另一個點的一個或兩個元組值。第一種情況,(x, 0),匹配任何點的y值0和分配點的x值暫時不變。同樣的,第二種情況下,例(0,讓y)匹配任何點的x值0和分配點的y y值暫時不變。
在聲明臨時常量之后,可以在case的代碼塊中使用它們。在這里,它們被用來打印點的分類。
此switch語句沒有默認情況。最后一個case, case let (x, y)聲明了一個由兩個占位符常量組成的元組,它可以匹配任何值。因為另一個point總是由兩個值組成的元組,所以這種情況匹配所有可能的剩余值,并且不需要使用缺省情況來使switch語句詳盡。
where
swift 用例可以使用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),或者兩者都不是。
這三個開關(guān)用例聲明占位符常量x和y,它們臨時接受來自yetAnotherPoint的兩個元組值。這些常量用作where子句的一部分,用于創(chuàng)建動態(tài)過濾器。只有當where子句的條件為true時,switch case才匹配point的當前值。
與前面的示例一樣,最后一個用例匹配所有可能的剩余值,因此不需要使用缺省用例來使switch語句詳盡。
Compound Cases 復合case
switch中共享同一主體的多個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軸上的點。這兩種模式都包含一個用于距離的綁定,而距離在這兩種模式中都是整數(shù)——這意味著案例主體中的代碼總是可以訪問一個用于距離的值。
Control Transfer Statements 控制轉(zhuǎn)移語句
控件轉(zhuǎn)移語句通過將控件從一段代碼轉(zhuǎn)移到另一段代碼來更改代碼執(zhí)行的順序。Swift中有五個控制轉(zhuǎn)移語句:
- continue
- break
- fallthrough
- return
- throw
下面將描述continue、break和fallthrough語句。return語句在函數(shù)中描述,throw語句在使用拋出函數(shù)傳播錯誤中描述。
Continue
continue語句告訴循環(huán)停止正在執(zhí)行的操作,并在循環(huán)的下一個迭代開始時重新開始。它說“我已經(jīng)完成了當前循環(huán)迭代”,而沒有完全離開循環(huán)。
簡單例子:
for i in 1...10 {
if i%2 == 0 {
continue
}
print("只打印奇數(shù):\(i)")
}
輸出結(jié)果:
只打印奇數(shù):1
只打印奇數(shù):3
只打印奇數(shù):5
只打印奇數(shù):7
只打印奇數(shù):9
Break
break語句立即終止整個控制流語句的執(zhí)行。當您希望提前終止switch或loop語句的執(zhí)行時,可以在switch或loop語句中使用break語句。
Break in a Loop Statement
當在循環(huán)語句中使用break時,它會立即結(jié)束循環(huán)的執(zhí)行,并在循環(huán)的右大括號(})之后將控制權(quán)轉(zhuǎn)移到代碼中。不再執(zhí)行當前循環(huán)迭代中的代碼,也不再啟動循環(huán)的進一步迭代。
Break in a Switch Statement
當在switch語句中使用break時,break會導致switch語句立即結(jié)束執(zhí)行,并在switch語句的右括號(})之后將控制權(quán)轉(zhuǎn)移到代碼中。
此行為可用于匹配和忽略switch語句中的一個或多個案例。因為Swift的switch聲明是詳盡的,并且不允許空案例,所以有時有必要故意匹配和忽略一個案例,以便使您的意圖明確。通過將break語句作為要忽略的整個案例體來實現(xiàn)這一點。當這種情況與switch語句匹配時,case中的break語句立即終止switch語句的執(zhí)行。
只包含注釋的切換用例被報告為編譯時錯誤。注釋不是語句,不會導致忽略開關(guān)用例。始終使用break語句來忽略開關(guān)情況。
下面的示例打開一個字符值,并確定它是否用四種語言之一表示數(shù)字符號。為了簡單起見,一個開關(guān)用例包含多個值。
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."
本例檢查數(shù)字符號,以確定數(shù)字1到4是拉丁、阿拉伯、漢語還是泰國符號。如果找到匹配項,switch語句的一個用例將設(shè)置一個可選的Int?一個名為possibleIntegerValue的變量,將其轉(zhuǎn)換為適當?shù)恼麛?shù)值。
switch語句執(zhí)行完成后,示例使用可選綁定來確定是否找到了一個值。由于是可選類型,possible leintegervalue變量的初始值隱式為nil,因此,只有在switch語句的前四種情況之一將possible leintegervalue設(shè)置為實際值時,可選綁定才會成功。
因為在上面的例子中不可能列出所有可能的字符值,所以默認情況下處理任何不匹配的字符。這個默認情況不需要執(zhí)行任何操作,因此它的主體是一個break語句。一旦匹配了默認情況,break語句將結(jié)束switch語句的執(zhí)行,代碼執(zhí)行將從if let語句繼續(xù)。
Fallthrough
在Swift中,switch語句不會從每個case的底部進入下一個case。也就是說,當?shù)谝粋€匹配的用例完成時,整個switch語句就完成了它的執(zhí)行。相反,C要求您在每個switch case的末尾插入一個顯式的break語句,以防止漏接。避免默認錯誤意味著Swift switch語句比C語言中的對應(yīng)語句更加簡潔和可預測,因此它們避免了錯誤地執(zhí)行多個開關(guān)用例。
如果您需要c風格的fallthrough行為,您可以使用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)
輸出結(jié)果為:
The number 5 is a prime number, and also an integer.
本例聲明了一個名為description的新字符串變量,并為其賦值。然后,函數(shù)使用switch語句考慮integerToDescribe的值。如果integerToDescribe的值是列表中的質(zhì)數(shù)之一,則該函數(shù)將文本附加到描述的末尾,以注意該數(shù)字是質(zhì)數(shù)。然后,它還使用fallthrough關(guān)鍵字“陷入”默認情況。默認情況下,在描述的末尾添加一些額外的文本,switch語句就完成了。
除非integerToDescribe的值在已知素數(shù)列表中,否則第一個開關(guān)情況根本不匹配它。因為沒有其他特定的情況,integerToDescribe與默認情況匹配。
fallthrough關(guān)鍵字不檢查導致執(zhí)行陷入的切換用例的用例條件。fallthrough關(guān)鍵字只是導致代碼執(zhí)行直接移動到下一個case(或默認case)塊中的語句,就像在C的標準switch語句行為中一樣。
Labeled Statements 標簽申明語句
在Swift中,您可以將循環(huán)和條件語句嵌套在其他循環(huán)和條件語句中,以創(chuàng)建復雜的控制流結(jié)構(gòu)。然而,循環(huán)和條件語句都可以使用break語句提前結(jié)束它們的執(zhí)行。因此,有時顯式說明希望終止哪個循環(huán)或條件語句是有用的。類似地,如果您有多個嵌套循環(huán),那么顯式地說明continue語句應(yīng)該影響哪個循環(huán)是很有用的。
要實現(xiàn)這些目標,可以使用語句標簽標記循環(huán)語句或條件語句。對于條件語句,可以使用帶有break語句的語句標簽來結(jié)束已標記語句的執(zhí)行。對于循環(huán)語句,您可以使用帶有break或continue語句的語句標簽來結(jié)束或繼續(xù)執(zhí)行帶標簽的語句。
帶標簽的語句通過將標簽放在與語句的介紹人關(guān)鍵字相同的行上,后跟冒號來表示。下面是while循環(huán)的一個語法例子,盡管所有循環(huán)和switch語句的原則都是相同的:
labelName: while condition {
statements
}
下面的例子使用了break和continue語句,游戲規(guī)則為
如果一個骰子擲出的點數(shù)超過了25平方,你必須再擲一次,直到你擲出的點數(shù)達到25平方的點數(shù)為止。
這個版本的游戲使用while循環(huán)和switch語句來實現(xiàn)游戲的邏輯。while循環(huán)有一個名為gameLoop的語句標簽,表示它是snake和ladder游戲的主游戲循環(huán)。
while循環(huán)的條件是while square != finalSquare,為了反映您必須精確地降落在square 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
var square = 0
var diceRoll = 0
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!")
骰子在每個循環(huán)的開始滾動。循環(huán)不是立即移動玩家,而是使用switch語句來考慮移動的結(jié)果,并確定是否允許移動:
如果擲骰子將玩家移到最后一個方塊,游戲就結(jié)束了。break gameLoop語句將控制權(quán)轉(zhuǎn)移到while循環(huán)之外的第一行代碼,從而結(jié)束游戲。
如果擲骰子將玩家移出最后一個方塊,則該移動無效,玩家需要再次擲骰。continue gameLoop語句結(jié)束當前while循環(huán)迭代并開始循環(huán)的下一個迭代。
在所有其他情況下,擲骰子都是有效的。玩家通過雙棋盤向前移動,游戲邏輯檢查是否有蛇和梯子。然后循環(huán)結(jié)束,控制返回到while條件,以決定是否需要進行另一輪。
如果上面的break語句沒有使用gameLoop標簽,它將跳出switch語句,而不是while語句。使用gameLoop標簽可以清楚地表明應(yīng)該終止哪個控制語句。
在調(diào)用continue gameLoop以跳轉(zhuǎn)到循環(huán)的下一個迭代時,并不一定要使用gameLoop標簽。游戲中只有一個循環(huán),因此對于continue語句將影響哪個循環(huán)沒有歧義。但是,使用帶有continue語句的gameLoop標簽并沒有什么害處。這樣做與標簽和break語句的使用是一致的,并有助于使游戲的邏輯更清晰地閱讀和理解。
Early Exit 提前退出
與if語句類似,guard語句根據(jù)表達式的布爾值執(zhí)行語句。使用guard語句要求條件必須為true,以便在執(zhí)行g(shù)uard語句之后執(zhí)行代碼。與if語句不同,guard語句總是有一個else子句——如果條件不為真,則在else子句中執(zhí)行代碼。
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語句的右括號之后繼續(xù)執(zhí)行代碼。使用可選綁定作為條件的一部分為值賦值的任何變量或常量都可用于guard語句出現(xiàn)的代碼塊的其余部分。
如果不滿足該條件,則執(zhí)行else分支中的代碼。該分支必須轉(zhuǎn)移控件才能退出出現(xiàn)guard語句的代碼塊。它可以通過控制傳輸語句(如return、break、continue或throw)來實現(xiàn)這一點,也可以調(diào)用不返回的函數(shù)或方法,如fatalError(_:file:line:)。
與使用if語句進行相同的檢查相比,為需求使用guard語句可以提高代碼的可讀性。它允許您編寫通常執(zhí)行的代碼,而不需要將其包裝在else塊中,并且允許您將處理違反需求的代碼放在需求旁邊。
Checking API Availability 檢查api可用性
Swift內(nèi)置了對API可用性檢查的支持,這確保您不會意外地使用在給定部署目標上不可用的API。
編譯器使用SDK中的可用性信息來驗證代碼中使用的所有api在項目指定的部署目標上都是可用的。如果你試圖使用一個不可用的API, Swift會在編譯時報告一個錯誤。
您可以在if或guard語句中使用可用性條件來有條件地執(zhí)行代碼塊,這取決于您希望使用的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語句的主體只在ios10及以后版本中執(zhí)行;在macOS中,只有在macOS 10.12或更高版本中。最后一個參數(shù)*是必需的,并指定在任何其他平臺上,if的主體在目標指定的最小部署目標上執(zhí)行。
if #available(platform name version, ..., *) {
statements to execute if the APIs are available
} else {
fallback statements to execute if the APIs are unavailable
}