swift-控制流5

前言

Swift提供了多種流程控制結構,包括可以多次執行任務的while循環,基于特定條件選擇執行不同代碼分支的if、guard和switch語句,還有控制流程跳轉到其他代碼位置的break和continue語句。

Swift 還提供了for-in循環,用來更簡單地遍歷數組(Array),字典(Dictionary),區間(Range),字符串(String)和其他序列類型。

Swift 的switch語句比 C 語言中更加強大。case 還可以匹配很多不同的模式,包括范圍匹配,元組(tuple)和特定類型匹配。switch語句的 case 中匹配的值可以聲明為臨時常量或變量,在 case 作用域內使用,也可以配合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!

你也可以通過遍歷一個字典來訪問它的鍵值對

   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 循環還可以使用數字范圍

   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

使用 stride(from:to:by:) 函數跳過不需要的標記。 后面的區間未閉合

let minuteInterval = 5
for tickMark in stride(from: 0, to: 60, 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 condition {  
   statements
}

Repeat-While

repeat {
   statements
} while condition

條件語句

根據特定的條件執行特定的代碼通常是十分有用的。當錯誤發生時,你可能想運行額外的代碼;或者,當值太大或太小時,向用戶顯示一條消息。要實現這些功能,你就需要使用條件語句。

Swift 提供兩種類型的條件語句:if語句和switch語句。通常,當條件較為簡單且可能的情況很少時,使用if語句。而switch語句更適用于條件較復雜、有更多排列組合的時候。并且switch在需要用到模式匹配(pattern-matching)的情況下會更有用。

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

Switch

switch語句會嘗試把某個值與若干個模式(pattern)進行匹配。根據第一個匹配成功的模式,switch語句會執行對應的代碼。當有可能的情況較多時,通常用switch語句替換if語句

匹配類型豐富

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分支中顯式地使用break語句

復合匹配

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

區間匹配

case 分支的模式也可以是一個值的區間。下面的例子展示了如何使用區間匹配來輸出任意數字對應的自然語言格式:

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

元組

我們可以使用元組在同一個switch語句中測試多個值。元組中的元素可以是值,也可以是區間。另外,使用下劃線(_)來匹配所有可能的值。

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"

值綁定(Value Bindings)

case 分支允許將匹配的值聲明為臨時常量或變量,并且在case分支體內使用 —— 這種行為被稱為值綁定(value binding),因為匹配的值在case分支體內,與臨時的常量或變量綁定。

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"

Where

case 分支的模式可以使用where語句來判斷額外的條件。

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 有五種控制轉移語句:
continue. break. fallthrough. return. throw

Continue

continue語句告訴一個循環體立刻停止本次循環,重新開始下次循環。就好像在說“本次循環我已經執行完了”,但是并不會離開整個循環體。

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"

Break

break語句會立刻結束整個控制流的執行。break 可以在 switch 或循環語句中使用,用來提前結束switch或循環語句。

注意: 當一個switch分支僅僅包含注釋時,會被報編譯時錯誤。注釋不是代碼語句而且也不能讓switch分支達到被忽略的效果。你應該使用break來忽略某個分支。

let numberSymbol: Character = "三"  // 簡體中文里的數字 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."

貫穿

在 Swift 里,switch語句不會從上一個 case 分支跳轉到下一個 case 分支中。相反,只要第一個匹配到的 case 分支完成了它需要執行的語句,整個switch代碼塊完成了它的執行。

如果你確實需要 C 風格的貫穿的特性,你可以在每個需要該特性的 case 分支中使用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."

fallthrough關鍵字不會檢查它下一個將會落入執行的 case 中的匹配條件。fallthrough簡單地使代碼繼續連接到下一個 case 中的代碼,這和 C 語言標準中的switch語句特性是一樣的。

帶標簽的語句

你可以使用標簽(statement label)來標記一個循環體或者條件語句,對于一個條件語句,你可以使用break加標簽的方式,來結束這個被標記的語句。對于一個循環語句,你可以使用break或者continue加標簽,來結束或者繼續這條被標記語句的執行。

聲明一個帶標簽的語句是通過在該語句的關鍵詞的同一行前面放置一個標簽,作為這個語句的前導關鍵字(introducor keyword),并且該標簽后面跟隨一個冒號。下面是一個針對while循環體的標簽語法,同樣的規則適用于所有的循環體和條件語句。

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

提前退出

像if語句一樣,guard的執行取決于一個表達式的布爾值。我們可以使用guard語句來要求條件必須為真時,以執行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(["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語句的條件被滿足,則繼續執行guard語句大括號后的代碼。將變量或者常量的可選綁定作為guard語句的條件,都可以保護guard語句后面的代碼。

如果條件不被滿足,在else分支上的代碼就會被執行。這個分支必須轉移控制以退出guard語句出現的代碼段。它可以用控制轉移語句如return,break,continue或者throw做這件事,或者調用一個不返回的方法或函數,例如fatalError()。

檢測 API 可用性

Swift內置支持檢查 API 可用性,這可以確保我們不會在當前部署機器上,不小心地使用了不可用的API。

編譯器使用 SDK 中的可用信息來驗證我們的代碼中使用的所有 API 在項目指定的部署目標上是否可用。如果我們嘗試使用一個不可用的 API,Swift 會在編譯時報錯。

我們在if或guard語句中使用可用性條件(availability condition)去有條件的執行一段代碼,來在運行時判斷調用的API是否可用。編譯器使用從可用性條件語句中獲取的信息去驗證,在這個代碼塊中調用的 API 是否可用。

if #available(iOS 10, macOS 10.12, *) {
    // 在 iOS 使用 iOS 10 的 API, 在 macOS 使用 macOS 10.12 的 API
} else {
    // 使用先前版本的 iOS 和 macOS 的 API
}

以上可用性條件指定,if語句的代碼塊僅僅在 iOS 10 或 macOS 10.12 及更高版本才運行。最后一個參數,*,是必須的,用于指定在所有其它平臺中,如果版本號高于你的設備指定的最低版本,if語句的代碼塊將會運行。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,185評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,656評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,647評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,446評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,951評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,189評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,718評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,800評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,419評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,420評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,755評論 2 371

推薦閱讀更多精彩內容