Swift學習筆記 (十三) 控制流(流程控制)

控制轉移語句

控制轉移語句改變你代碼的執⾏順序,通過它可以實現代碼的跳轉。Swift 有五種控制轉移語句:

  1. continue
  2. break
  3. fallthrough
  4. return
  5. throw

我們將會在下面討論 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”

在上⾯的代碼中,只要匹配到元音字母或者空格字符,就調用 continue 語句,使本次循環結束,重新開始下次循環。 這種⾏爲

使 switch 匹配到元音字母和空格字符時不做處理,⽽不是讓每一個匹配到的字符都被打印。

 

Break

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

 

循環語句中的 break

當在一個循環體中使用 break 時,會立刻中斷該循環體的執行,然後跳轉到表示循環體結束的大括號( } )後的第一 ⾏代碼。不會

再有本次循環的代碼被執⾏,也不會再有下次的循環產生。

 

Switch 語句中的 break

當在一個 switch 代碼塊中使用 break 時,會立即中斷該 switch 代碼塊的執⾏,並且跳轉到表示 switch 代碼塊結束的大括號( } 

)後的第一⾏代碼。

這種特性可以被用來匹配或者忽略一個或多個分支。因爲 Swift 的 switch 需要包含所有的分⽀⽽且不允許有爲空的分支,有時

爲了使你的意圖更明顯,需要特意匹配或者忽略某個分支。那麼當你想忽略某個分支時,可以在該分支內寫上 break 語句。當那

個分支被匹配到時,分支內的 break 語句立即結束 switch 代碼塊。

注意

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

使用 break 來忽略某個分支。

下⾯的例子通過 switch 來判斷一個 Character 值是否代表下⾯四種語言之一。爲了簡潔,多個值被包含在了同一個分支情況中。

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

這個例子檢查 numberSymbol 是否是拉丁,阿拉伯,中⽂或者泰語中的 1 到 4 之一。如果被匹配到,該 switch 分支語句給 Int? 

類型變量 possibleIntegerValue 設置一個整數值。

當 switch 代碼塊執⾏完後,接下來的代碼通過使用可選綁定來判斷 possibleIntegerValue 是否曾經被設置過值。因爲是可選類型

的緣故, possibleIntegerValue 有一個隱式的初始值 nil ,所以僅當 possibleIntegerValue 曾被 switch 代碼塊的前四個分支中的

某個設置過一個值時,可選的綁定纔會被判定爲成功。

在上面的例子中,想要把 Character 所有的的可能性都枚舉出來是不現實的,所以使用 default 分支來包含所有上面沒有匹配到

字符的情況。由於這個 default 分支不需要執⾏任何動作,所以它只寫了一條 break 語句。一旦落入 到 default 分支中後, break 

語句就完成了該分支的所有代碼操作,代碼繼續向下,開始執⾏ if let 語句。

 

貫穿(Fallthrough)

在 Swift ⾥, switch 語句不會從上一個 case 分支跳轉到下一個 case 分支中。相反,只要第一個匹配到的 case 分支 完成了它

需要執⾏的語句,整個 switch 代碼塊就完成了它的執⾏。相比之下,C 語言要求你顯式地插入 break 語句到每個 case 分支的

末尾來阻止自動落入到下一個 case 分支中。Swift 的這種避免默認落入到下一個分支中的特性意味着它的 switch 功能要比 C 語

⾔的更加清晰和可預測,可以避免無意識地執⾏多個 case 分支從而引發的錯誤。

如果你確實需要 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.”

這個例子定義了一個 String 類型的變量 description 並且給它設置了一個初始值。函數使用 switch 邏輯來判斷 

integerToDescribe 變量的值。當 integerToDescribe 的值屬於列表中的質數之一時,該函數在 description 後添加一段文字,來

表明這個數字是一個質數。然後它使用 fallthrough 關鍵字來“貫穿”到default 分支中。 default 分支在 description 的最後添加一

段額外的文字,至此 switch 代碼塊執⾏完了。

如果 integerToDescribe 的值不屬於列表中的任何質數,那麼它不會匹配到第一個 switch 分支。⽽這⾥沒有其他特別的分支情

況,所以 integerToDescribe 被匹配到 default 分支中。

當 switch 代碼塊執⾏完後,使用 print(_:separator:terminator:) 函數打印該數字的描述。在這個例子中,數字 5 被準確的識別爲

了一個質數。

注意

fallthrough 關鍵字不會檢查它下一個將會落入執⾏的 case 中的匹配條件。 fallthrough 簡單地使代碼繼續連接到下一個 case

中的代碼,這和 C 語言標準中的 switch 語句特性是一樣的。

 

帶標籤的語句

在 Swift 中,你可以在循環體和條件語句中嵌套循環體和條件語句來創造複雜的控制流結構。並且,循環體和條件語句都可以使

用 break 語句來提前結束整個代碼塊。因此,顯式地指明 break 語句想要終止的是哪個循環體或者條件語句,會很有用。類似

地,如果你有許多嵌套的循環體,顯式指明 continue 語句想要影響哪一個循環體也會非常有用。

爲了實現這個目的,你可以使用標籤(statement label)來標記一個循環體或者條件語句,對於一個條件語句,你可以使用 break 

加標籤的⽅式,來結束這個被標記的語句。對於一個循環語句,你可以使用 break 或者 continue 加標籤,來結束或者繼續這條

被標記語句的執⾏。

聲明一個帶標籤的語句是通過在該語句的關鍵詞的同一行前面放置一個標籤,作爲這個語句的前導關鍵字(introducor

keyword),並且該標籤後面跟隨一個冒號。下⾯是⼀個針對 while 循環體的標籤語法,同樣的規則適用於所有的循環體和條件語

句。

label name: while condition {

           statements

}

下⾯的例子是前⾯章節中蛇和梯⼦的適配版本,在此版本中,我們將使用一個帶有標籤的 while 循環體中調用 break 和 continue 

語句。

這次,遊戲增加了一條額外的規則:

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

如果某次擲骰子使你的移動超出第 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 ,來表明它是遊戲的主循

環。該 while 循環體的條件判斷語句是 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!")

每次循環迭代開始時擲骰子。與之前玩家擲完骰子就立即移動不同,這⾥使⽤了 switch 語句來考慮每次移動可能產⽣的結果,

從而決定玩家本次是否能夠移動。

如果骰子數剛好使玩家移動到最終的方格里,遊戲結束。 break gameLoop 語句跳轉控制去執⾏ while 循環體後的第一⾏代碼,

意味着遊戲結束。 如果骰子數將會使玩家的移動超出最後的方格,那麼這種移動是不合法的,玩家需要重新擲骰子。 continue

gameLoop 語句結束本次 while 循環,開始下一次循環。 在剩餘的所有情況中,骰子數產生的都是合法的移動。玩家向前移動 

diceRoll 個方格,然後遊戲邏輯再處理 家當前是否處於蛇頭或者梯子的底部。接着本次循環結束,控制跳轉到 while 循環體的條

件判斷語句處,再決定是否需要繼續執行下次循環。

注意

如果上述的 break 語句沒有使用 gameLoop 標籤,那麼它將會中斷 switch 語句⽽不是 while 循環。使⽤ gameLoop 標籤清晰

的表明了 break 想要中斷的是哪個代碼塊。

同時請注意,當調用 continue gameLoop 去跳轉到下一次循環迭代時,這⾥使⽤ gameLoop 標籤並不是嚴格必須的。因爲在這

個遊戲中,只有一個循環體,所以 continue 語句會影響到哪個循環體是沒有歧義的。然而,continue 語句使⽤ gameLoop 標

籤也是沒有危害的。這樣做符合標籤的使用規則,同時參照旁邊的 break gameLoop ,能夠使遊戲的邏輯更加清晰和易於理

解。

 

提前退出 

像 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(person: ["name": "John"])

// 輸出“Hello John!”

// 輸出“I hope the weather is nice near you.”

greet(person: ["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() 。

相比於可以實現同樣功能的 if 語句,按需使用 guard 語句會提升我們代碼的可讀性。它可以使你的代碼連貫的被執行而不需要將

它包在 else 塊中,它可以使你在緊鄰條件判斷的地方,處理違規的情況。

 

檢測 API 可⽤用性

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

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

不可⽤的 API,Swift 會在編譯時報錯。

我們在 if 或 guard 語句中使用可用性條件(availability condition) 去有條件的執⾏一段代碼,來在運⾏時判斷調用的 API 是否可

用。編譯器使用從可用性條件語句中獲取的信息去驗證,在這個代碼塊中調用的 API 是否可用。

if #available(iOS 10, macOS 10.12, *) {

             // 在 iOS 使用 iOS10 的 API, 在 macOS 使用 macOS10.12 的 API

} else {
            // 使用先前版本的 iOS 和 macOS 的 API

}

以上可用性條件指定, if 語句的代碼塊僅僅在 iOS 10 或 macOS 10.12 及更高版本才運⾏。最後一個參數, * ,是必須的,⽤於

指定在所有其它平臺中,如果版本號高於你的設備指定的最低版本,if 語句的代碼塊將會運行。

在它一般的形式中,可用性條件使⽤了一個平臺名字和版本的列表。平臺名字可以是 iOS , macOS , watchOS 和tvOS ——請

訪問聲明屬性來獲取完整列表。除了指定像 iOS 8 或 macOS 10.10 的大版本號,也可以指定像 iOS 11.2.6 以及 macOS 10.13.3 的

小版本號。

if #available(平臺名稱 版本號, ..., *) {

              APIs 可⽤,語句將執⾏

} else {
            APIs 不可用,語句將執⾏的回退語句

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章