<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 錯誤處理 *錯誤處理(Error handling)* 是響應錯誤以及從錯誤中恢復的過程。Swift 在運行時提供了拋出、捕獲、傳遞和操作可恢復錯誤(recoverable errors)的一等支持(first-class support)。 某些操作無法保證總是執行完所有代碼或生成有用的結果。可選類型用來表示值缺失,但是當某個操作失敗時,理解造成失敗的原因有助于你的代碼作出相應的應對。 舉個例子,假如有個從磁盤上的某個文件讀取數據并進行處理的任務,該任務會有多種可能失敗的情況,包括指定路徑下文件并不存在,文件不具有可讀權限,或者文件編碼格式不兼容。區分這些不同的失敗情況可以讓程序處理并解決某些錯誤,然后把它解決不了的錯誤報告給用戶。 > 注意 > > Swift 中的錯誤處理涉及到錯誤處理模式,這會用到 Cocoa 和 Objective-C 中的 `NSError`。更多詳情參見 [用 Swift 解決 Cocoa 錯誤](https://developer.apple.com/documentation/swift/cocoa_design_patterns/handling_cocoa_errors_in_swift)。 ## 表示與拋出錯誤 {#representing-and-throwing-errors} 在 Swift 中,錯誤用遵循 `Error` 協議的類型的值來表示。這個空協議表明該類型可以用于錯誤處理。 Swift 的枚舉類型尤為適合構建一組相關的錯誤狀態,枚舉的關聯值還可以提供錯誤狀態的額外信息。例如,在游戲中操作自動販賣機時,你可以這樣表示可能會出現的錯誤狀態: ```swift enum VendingMachineError: Error { case invalidSelection //選擇無效 case insufficientFunds(coinsNeeded: Int) //金額不足 case outOfStock //缺貨 } ``` 拋出一個錯誤可以讓你表明有意外情況發生,導致正常的執行流程無法繼續執行。拋出錯誤使用 `throw` 語句。例如,下面的代碼拋出一個錯誤,提示販賣機還需要 `5` 個硬幣: ```swift throw VendingMachineError.insufficientFunds(coinsNeeded: 5) ``` ## 處理錯誤 {#handling-errors} 某個錯誤被拋出時,附近的某部分代碼必須負責處理這個錯誤,例如糾正這個問題、嘗試另外一種方式、或是向用戶報告錯誤。 Swift 中有 `4` 種處理錯誤的方式。你可以把函數拋出的錯誤傳遞給調用此函數的代碼、用 `do-catch` 語句處理錯誤、將錯誤作為可選類型處理、或者斷言此錯誤根本不會發生。每種方式在下面的小節中都有描述。 當一個函數拋出一個錯誤時,你的程序流程會發生改變,所以重要的是你能迅速識別代碼中會拋出錯誤的地方。為了標識出這些地方,在調用一個能拋出錯誤的函數、方法或者構造器之前,加上 `try` 關鍵字,或者 `try?` 或 `try!` 這種變體。這些關鍵字在下面的小節中有具體講解。 > 注意 > > Swift 中的錯誤處理和其他語言中用 `try`,`catch` 和 `throw` 進行異常處理很像。和其他語言中(包括 Objective-C )的異常處理不同的是,Swift 中的錯誤處理并不涉及解除調用棧,這是一個計算代價高昂的過程。就此而言,`throw` 語句的性能特性是可以和 `return` 語句相媲美的。 ### 用 throwing 函數傳遞錯誤 {#propagating-errors-using-throwing-functions} 為了表示一個函數、方法或構造器可以拋出錯誤,在函數聲明的參數之后加上 `throws` 關鍵字。一個標有 `throws` 關鍵字的函數被稱作 *throwing 函數*。如果這個函數指明了返回值類型,`throws` 關鍵詞需要寫在返回箭頭(`->`)的前面。 ```swift func canThrowErrors() throws -> String func cannotThrowErrors() -> String ``` 一個 throwing 函數可以在其內部拋出錯誤,并將錯誤傳遞到函數被調用時的作用域。 > 注意 > > 只有 throwing 函數可以傳遞錯誤。任何在某個非 throwing 函數內部拋出的錯誤只能在函數內部處理。 下面的例子中,`VendingMachine` 類有一個 `vend(itemNamed:)` 方法,如果請求的物品不存在、缺貨或者投入金額小于物品價格,該方法就會拋出一個相應的 `VendingMachineError`: ```swift struct Item { var price: Int var count: Int } class VendingMachine { var inventory = [ "Candy Bar": Item(price: 12, count: 7), "Chips": Item(price: 10, count: 4), "Pretzels": Item(price: 7, count: 11) ] var coinsDeposited = 0 func vend(itemNamed name: String) throws { guard let item = inventory[name] else { throw VendingMachineError.invalidSelection } guard item.count > 0 else { throw VendingMachineError.outOfStock } guard item.price <= coinsDeposited else { throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited) } coinsDeposited -= item.price var newItem = item newItem.count -= 1 inventory[name] = newItem print("Dispensing \(name)") } } ``` 在 `vend(itemNamed:)` 方法的實現中使用了 `guard` 語句來確保在購買某個物品所需的條件中有任一條件不滿足時,能提前退出方法并拋出相應的錯誤。由于 `throw` 語句會立即退出方法,所以物品只有在所有條件都滿足時才會被售出。 因為 `vend(itemNamed:)` 方法會傳遞出它拋出的任何錯誤,在你的代碼中調用此方法的地方,必須要么直接處理這些錯誤——使用 `do-catch` 語句,`try?` 或 `try!`;要么繼續將這些錯誤傳遞下去。例如下面例子中,`buyFavoriteSnack(person:vendingMachine:)` 同樣是一個 throwing 函數,任何由 `vend(itemNamed:)` 方法拋出的錯誤會一直被傳遞到 `buyFavoriteSnack(person:vendingMachine:)` 函數被調用的地方。 ```swift let favoriteSnacks = [ "Alice": "Chips", "Bob": "Licorice", "Eve": "Pretzels", ] func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws { let snackName = favoriteSnacks[person] ?? "Candy Bar" try vendingMachine.vend(itemNamed: snackName) } ``` 上例中,`buyFavoriteSnack(person:vendingMachine:)` 函數會查找某人最喜歡的零食,并通過調用 `vend(itemNamed:)` 方法來嘗試為他們購買。因為 `vend(itemNamed:)` 方法能拋出錯誤,所以在調用它的時候在它前面加了 `try` 關鍵字。 `throwing` 構造器能像 `throwing` 函數一樣傳遞錯誤。例如下面代碼中的 `PurchasedSnack` 構造器在構造過程中調用了 throwing 函數,并且通過傳遞到它的調用者來處理這些錯誤。 ```swift struct PurchasedSnack { let name: String init(name: String, vendingMachine: VendingMachine) throws { try vendingMachine.vend(itemNamed: name) self.name = name } } ``` ### 用 Do-Catch 處理錯誤 {#handling-errors-using-do-Catch} 你可以使用一個 `do-catch` 語句運行一段閉包代碼來處理錯誤。如果在 `do` 子句中的代碼拋出了一個錯誤,這個錯誤會與 `catch` 子句做匹配,從而決定哪條子句能處理它。 下面是 `do-catch` 語句的一般形式: ```swift do { try expression statements } catch pattern 1 { statements } catch pattern 2 where condition { statements } catch pattern 3, pattern 4 where condition { statements } catch { statements } ``` 在 `catch` 后面寫一個匹配模式來表明這個子句能處理什么樣的錯誤。如果一條 `catch` 子句沒有指定匹配模式,那么這條子句可以匹配任何錯誤,并且把錯誤綁定到一個名字為 `error` 的局部常量。關于模式匹配的更多信息請參考 [模式](../03_language_reference/08_Patterns.md)。 舉例來說,下面的代碼處理了 `VendingMachineError` 枚舉類型的全部三種情況: ```swift var vendingMachine = VendingMachine() vendingMachine.coinsDeposited = 8 do { try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine) print("Success! Yum.") } catch VendingMachineError.invalidSelection { print("Invalid Selection.") } catch VendingMachineError.outOfStock { print("Out of Stock.") } catch VendingMachineError.insufficientFunds(let coinsNeeded) { print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.") } catch { print("Unexpected error: \(error).") } // 打印“Insufficient funds. Please insert an additional 2 coins.” ``` 上面的例子中,`buyFavoriteSnack(person:vendingMachine:)` 函數在一個 `try` 表達式中被調用,是因為它能拋出錯誤。如果錯誤被拋出,相應的執行會馬上轉移到 `catch` 子句中,并判斷這個錯誤是否要被繼續傳遞下去。如果錯誤沒有被匹配,它會被最后一個 `catch` 語句捕獲,并賦值給一個 `error` 常量。如果沒有錯誤被拋出,`do` 子句中余下的語句就會被執行。 `catch` 子句不必將 `do` 子句中的代碼所拋出的每一個可能的錯誤都作處理。如果所有 `catch` 子句都未處理錯誤,錯誤就會傳遞到周圍的作用域。然而,錯誤還是必須要被某個周圍的作用域處理的。在不會拋出錯誤的函數中,必須用 `do-catch` 語句處理錯誤。而能夠拋出錯誤的函數既可以使用 `do-catch` 語句處理,也可以讓調用方來處理錯誤。如果錯誤傳遞到了頂層作用域卻依然沒有被處理,你會得到一個運行時錯誤。 以下面的代碼為例,不是 `VendingMachineError` 中聲明的錯誤會在調用函數的地方被捕獲: ```swift func nourish(with item: String) throws { do { try vendingMachine.vend(itemNamed: item) } catch is VendingMachineError { print("Couldn't buy that from the vending machine.") } } do { try nourish(with: "Beet-Flavored Chips") } catch { print("Unexpected non-vending-machine-related error: \(error)") } // 打印“Couldn't buy that from the vending machine.” ``` 如果 `vend(itemNamed:)` 拋出的是一個 `VendingMachineError` 類型的錯誤,`nourish(with:)` 會打印一條消息,否則 `nourish(with:)` 會將錯誤拋給它的調用方。這個錯誤之后會被通用的 `catch` 語句捕獲。 另一種捕獲多個相關錯誤的方式是將它們放在 `catch` 后,通過逗號分隔。 例如: ```swift func eat(item: String) throws { do { try vendingMachine.vend(itemNamed: item) } catch VendingMachineError.invalidSelection, VendingMachineError.insufficientFunds, VendingMachineError.outOfStock { print("Invalid selection, out of stock, or not enough money.") } } ``` `eat(item:)` 函數捕獲了列出來的 `VendingMachine` 錯誤,且它的錯誤文本和列表的錯誤相關。如果列出來的三個錯誤中任意一個拋出,這個 `catch` 代碼塊就會打印信息。其他錯誤會傳遞到外面的作用域,包括以后可能添加的其他 `VendingMachine` 錯誤。 ### 將錯誤轉換成可選值 {#converting-errors-to-optional-values} 可以使用 `try?` 通過將錯誤轉換成一個可選值來處理錯誤。如果是在計算 `try?` 表達式時拋出錯誤,該表達式的結果就為 `nil`。例如,在下面的代碼中,`x` 和 `y` 有著相同的數值和等價的含義: ```swift func someThrowingFunction() throws -> Int { // ... } let x = try? someThrowingFunction() let y: Int? do { y = try someThrowingFunction() } catch { y = nil } ``` 如果 `someThrowingFunction()` 拋出一個錯誤,`x` 和 `y` 的值是 `nil`。否則 `x` 和 `y` 的值就是該函數的返回值。注意,無論 `someThrowingFunction()` 的返回值類型是什么類型,`x` 和 `y` 都是這個類型的可選類型。例子中此函數返回一個整型,所以 `x` 和 `y` 是可選整型。 如果你想對所有的錯誤都采用同樣的方式來處理,用 `try?` 就可以讓你寫出簡潔的錯誤處理代碼。例如,下面的代碼用幾種方式來獲取數據,如果所有方式都失敗了則返回 `nil`。 ```swift func fetchData() -> Data? { if let data = try? fetchDataFromDisk() { return data } if let data = try? fetchDataFromServer() { return data } return nil } ``` ### 禁用錯誤傳遞 {#disabling-error-propagation} 有時你知道某個 `throwing` 函數實際上在運行時是不會拋出錯誤的,在這種情況下,你可以在表達式前面寫 `try!` 來禁用錯誤傳遞,這會把調用包裝在一個不會有錯誤拋出的運行時斷言中。如果真的拋出了錯誤,你會得到一個運行時錯誤。 例如,下面的代碼使用了 `loadImage(atPath:)` 函數,該函數從給定的路徑加載圖片資源,如果圖片無法載入則拋出一個錯誤。在這種情況下,因為圖片是和應用綁定的,運行時不會有錯誤拋出,所以適合禁用錯誤傳遞。 ```swift let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg") ``` ## 指定清理操作 {#specifying-cleanup-actions} 你可以使用 `defer` 語句在即將離開當前代碼塊時執行一系列語句。該語句讓你能執行一些必要的清理工作,不管是以何種方式離開當前代碼塊的——無論是由于拋出錯誤而離開,或是由于諸如 `return`、`break` 的語句。例如,你可以用 `defer` 語句來確保文件描述符得以關閉,以及手動分配的內存得以釋放。 `defer` 語句將代碼的執行延遲到當前的作用域退出之前。該語句由 `defer` 關鍵字和要被延遲執行的語句組成。延遲執行的語句不能包含任何控制轉移語句,例如 `break`、`return` 語句,或是拋出一個錯誤。延遲執行的操作會按照它們聲明的順序從后往前執行——也就是說,第一條 `defer` 語句中的代碼最后才執行,第二條 `defer` 語句中的代碼倒數第二個執行,以此類推。最后一條語句會第一個執行。 ```swift func processFile(filename: String) throws { if exists(filename) { let file = open(filename) defer { close(file) } while let line = try file.readline() { // 處理文件。 } // close(file) 會在這里被調用,即作用域的最后。 } } ``` 上面的代碼使用一條 `defer` 語句來確保 `open(_:)` 函數有一個相應的對 `close(_:)` 函數的調用。 > 注意 > > 即使沒有涉及到錯誤處理的代碼,你也可以使用 `defer` 語句。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看