在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ iOS/ 錯(cuò)誤處理
特性(Attributes)
Access Control 權(quán)限控制的黑與白
基本運(yùn)算符(Basic Operators)
基礎(chǔ)部分(The Basics)
閉包(Closures)
擴(kuò)展
泛型參數(shù)(Generic Parameters and Arguments)
訪問(wèn)控制和 protected
語(yǔ)句(Statements)
模式(Patterns)
WWDC 里面的那個(gè)“大炮打氣球”
關(guān)于語(yǔ)言參考(About the Language Reference)
語(yǔ)法總結(jié)(Summary of the Grammar)
嵌套類(lèi)型
類(lèi)型(Types)
Swift 初見(jiàn)(A Swift Tour)
泛型
枚舉(Enumerations)
高級(jí)運(yùn)算符
繼承
析構(gòu)過(guò)程
關(guān)于 Swift(About Swift)
訪問(wèn)控制
類(lèi)和結(jié)構(gòu)體
內(nèi)存安全
Swift 與 C 語(yǔ)言指針友好合作
協(xié)議
屬性(Properties)
可選類(lèi)型完美解決占位問(wèn)題
錯(cuò)誤處理
字符串和字符(Strings and Characters)
聲明(Declarations)
自動(dòng)引用計(jì)數(shù)
Swift 里的值類(lèi)型與引用類(lèi)型
表達(dá)式(Expressions)
Swift 文檔修訂歷史
造個(gè)類(lèi)型不是夢(mèng)-白話 Swift 類(lèi)型創(chuàng)建
歡迎使用 Swift
詞法結(jié)構(gòu)(Lexical Structure)
集合類(lèi)型(Collection Types)
下標(biāo)
方法(Methods)
可選鏈?zhǔn)秸{(diào)用
版本兼容性
類(lèi)型轉(zhuǎn)換
構(gòu)造過(guò)程
The Swift Programming Language 中文版
函數(shù)(Functions)
Swift 教程
控制流(Control Flow)

錯(cuò)誤處理


2.1 翻譯+校對(duì):lyojo ray16897188 2015-10-23 校對(duì):shanks 2015-10-24

2.2 翻譯+校對(duì):SketchK 2016-05-15

3.0 翻譯+校對(duì):shanks 2016-09-24

3.0.1,shanks,2016-11-13

4.0 翻譯+校對(duì):kemchenj 2017-09-21

4.1 翻譯+校對(duì):mylittleswift

本頁(yè)包含內(nèi)容:

錯(cuò)誤處理(Error handling)是響應(yīng)錯(cuò)誤以及從錯(cuò)誤中恢復(fù)的過(guò)程。Swift 提供了在運(yùn)行時(shí)對(duì)可恢復(fù)錯(cuò)誤的拋出、捕獲、傳遞和操作的一等公民支持。

某些操作無(wú)法保證總是執(zhí)行完所有代碼或總是生成有用的結(jié)果??蛇x類(lèi)型可用來(lái)表示值缺失,但是當(dāng)某個(gè)操作失敗時(shí),最好能得知失敗的原因,從而可以作出相應(yīng)的應(yīng)對(duì)。

舉個(gè)例子,假如有個(gè)從磁盤(pán)上的某個(gè)文件讀取數(shù)據(jù)并進(jìn)行處理的任務(wù),該任務(wù)會(huì)有多種可能失敗的情況,包括指定路徑下文件并不存在,文件不具有可讀權(quán)限,或者文件編碼格式不兼容。區(qū)分這些不同的失敗情況可以讓程序解決并處理某些錯(cuò)誤,然后把它解決不了的錯(cuò)誤報(bào)告給用戶。

注意

Swift 中的錯(cuò)誤處理涉及到錯(cuò)誤處理模式,這會(huì)用到 Cocoa 和 Objective-C 中的 NSError。關(guān)于這個(gè)類(lèi)的更多信息請(qǐng)參見(jiàn) Using Swift with Cocoa and Objective-C (Swift 4.1) 中的錯(cuò)誤處理。

表示并拋出錯(cuò)誤

在 Swift 中,錯(cuò)誤用符合 Error 協(xié)議的類(lèi)型的值來(lái)表示。這個(gè)空協(xié)議表明該類(lèi)型可以用于錯(cuò)誤處理。

Swift 的枚舉類(lèi)型尤為適合構(gòu)建一組相關(guān)的錯(cuò)誤狀態(tài),枚舉的關(guān)聯(lián)值還可以提供錯(cuò)誤狀態(tài)的額外信息。例如,你可以這樣表示在一個(gè)游戲中操作自動(dòng)販賣(mài)機(jī)時(shí)可能會(huì)出現(xiàn)的錯(cuò)誤狀態(tài):

enum VendingMachineError: Error {
    case invalidSelection                    //選擇無(wú)效
    case insufficientFunds(coinsNeeded: Int) //金額不足
    case outOfStock                          //缺貨
}

拋出一個(gè)錯(cuò)誤可以讓你表明有意外情況發(fā)生,導(dǎo)致正常的執(zhí)行流程無(wú)法繼續(xù)執(zhí)行。拋出錯(cuò)誤使用 throw 關(guān)鍵字。例如,下面的代碼拋出一個(gè)錯(cuò)誤,提示販賣(mài)機(jī)還需要 5 個(gè)硬幣:

throw VendingMachineError.insufficientFunds(coinsNeeded: 5)

處理錯(cuò)誤

某個(gè)錯(cuò)誤被拋出時(shí),附近的某部分代碼必須負(fù)責(zé)處理這個(gè)錯(cuò)誤,例如糾正這個(gè)問(wèn)題、嘗試另外一種方式、或是向用戶報(bào)告錯(cuò)誤。

Swift 中有 4 種處理錯(cuò)誤的方式。你可以把函數(shù)拋出的錯(cuò)誤傳遞給調(diào)用此函數(shù)的代碼、用 do-catch 語(yǔ)句處理錯(cuò)誤、將錯(cuò)誤作為可選類(lèi)型處理、或者斷言此錯(cuò)誤根本不會(huì)發(fā)生。每種方式在下面的小節(jié)中都有描述。

當(dāng)一個(gè)函數(shù)拋出一個(gè)錯(cuò)誤時(shí),你的程序流程會(huì)發(fā)生改變,所以重要的是你能迅速識(shí)別代碼中會(huì)拋出錯(cuò)誤的地方。為了標(biāo)識(shí)出這些地方,在調(diào)用一個(gè)能拋出錯(cuò)誤的函數(shù)、方法或者構(gòu)造器之前,加上 try 關(guān)鍵字,或者 try?try! 這種變體。這些關(guān)鍵字在下面的小節(jié)中有具體講解。

注意

Swift 中的錯(cuò)誤處理和其他語(yǔ)言中用 try,catchthrow 進(jìn)行異常處理很像。和其他語(yǔ)言中(包括 Objective-C )的異常處理不同的是,Swift 中的錯(cuò)誤處理并不涉及解除調(diào)用棧,這是一個(gè)計(jì)算代價(jià)高昂的過(guò)程。就此而言,throw 語(yǔ)句的性能特性是可以和 return 語(yǔ)句相媲美的。

用 throwing 函數(shù)傳遞錯(cuò)誤

為了表示一個(gè)函數(shù)、方法或構(gòu)造器可以拋出錯(cuò)誤,在函數(shù)聲明的參數(shù)列表之后加上 throws 關(guān)鍵字。一個(gè)標(biāo)有 throws 關(guān)鍵字的函數(shù)被稱(chēng)作throwing 函數(shù)。如果這個(gè)函數(shù)指明了返回值類(lèi)型,throws 關(guān)鍵詞需要寫(xiě)在箭頭(->)的前面。

func canThrowErrors() throws -> String
func cannotThrowErrors() -> String

一個(gè) throwing 函數(shù)可以在其內(nèi)部拋出錯(cuò)誤,并將錯(cuò)誤傳遞到函數(shù)被調(diào)用時(shí)的作用域。

注意

只有 throwing 函數(shù)可以傳遞錯(cuò)誤。任何在某個(gè)非 throwing 函數(shù)內(nèi)部拋出的錯(cuò)誤只能在函數(shù)內(nèi)部處理。

下面的例子中,VendingMachine 類(lèi)有一個(gè) vend(itemNamed:) 方法,如果請(qǐng)求的物品不存在、缺貨或者投入金額小于物品價(jià)格,該方法就會(huì)拋出一個(gè)相應(yīng)的 VendingMachineError

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 dispenseSnack(snack: String) {
        print("Dispensing \(snack)")
    }

    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:) 方法的實(shí)現(xiàn)中使用了 guard 語(yǔ)句來(lái)提前退出方法,確保在購(gòu)買(mǎi)某個(gè)物品所需的條件中,有任一條件不滿足時(shí),能提前退出方法并拋出相應(yīng)的錯(cuò)誤。由于 throw 語(yǔ)句會(huì)立即退出方法,所以物品只有在所有條件都滿足時(shí)才會(huì)被售出。

因?yàn)?vend(itemNamed:) 方法會(huì)傳遞出它拋出的任何錯(cuò)誤,在你的代碼中調(diào)用此方法的地方,必須要么直接處理這些錯(cuò)誤——使用 do-catch 語(yǔ)句,try?try!;要么繼續(xù)將這些錯(cuò)誤傳遞下去。例如下面例子中,buyFavoriteSnack(person:vendingMachine:) 同樣是一個(gè) throwing 函數(shù),任何由 vend(itemNamed:) 方法拋出的錯(cuò)誤會(huì)一直被傳遞到 buyFavoriteSnack(person:vendingMachine:) 函數(shù)被調(diào)用的地方。

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:) 函數(shù)會(huì)查找某人最喜歡的零食,并通過(guò)調(diào)用 vend(itemNamed:) 方法來(lái)嘗試為他們購(gòu)買(mǎi)。因?yàn)?vend(itemNamed:) 方法能拋出錯(cuò)誤,所以在調(diào)用的它時(shí)候在它前面加了 try 關(guān)鍵字。

throwing 構(gòu)造器能像 throwing 函數(shù)一樣傳遞錯(cuò)誤。例如下面代碼中的 PurchasedSnack 構(gòu)造器在構(gòu)造過(guò)程中調(diào)用了 throwing 函數(shù),并且通過(guò)傳遞到它的調(diào)用者來(lái)處理這些錯(cuò)誤。

struct PurchasedSnack {
    let name: String
    init(name: String, vendingMachine: VendingMachine) throws {
        try vendingMachine.vend(itemNamed: name)
        self.name = name
    }
}

用 Do-Catch 處理錯(cuò)誤

你可以使用一個(gè) do-catch 語(yǔ)句運(yùn)行一段閉包代碼來(lái)處理錯(cuò)誤。如果在 do 子句中的代碼拋出了一個(gè)錯(cuò)誤,這個(gè)錯(cuò)誤會(huì)與 catch 子句做匹配,從而決定哪條子句能處理它。

下面是 do-catch 語(yǔ)句的一般形式:

do {
    try expression
    statements
} catch pattern 1 {
    statements
} catch pattern 2 where condition {
    statements
}

catch 后面寫(xiě)一個(gè)匹配模式來(lái)表明這個(gè)子句能處理什么樣的錯(cuò)誤。如果一條 catch 子句沒(méi)有指定匹配模式,那么這條子句可以匹配任何錯(cuò)誤,并且把錯(cuò)誤綁定到一個(gè)名字為 error 的局部常量。關(guān)于模式匹配的更多信息請(qǐng)參考 模式。

catch 子句不必將 do 子句中的代碼所拋出的每一個(gè)可能的錯(cuò)誤都作處理。如果所有 catch 子句都未處理錯(cuò)誤,錯(cuò)誤就會(huì)傳遞到周?chē)淖饔糜颉H欢?,錯(cuò)誤還是必須要被某個(gè)周?chē)淖饔糜蛱幚淼摹词且粋€(gè)外圍的 do-catch 錯(cuò)誤處理語(yǔ)句,要么是一個(gè) throwing 函數(shù)的內(nèi)部。舉例來(lái)說(shuō),下面的代碼處理了 VendingMachineError 枚舉類(lèi)型的全部枚舉值,但是所有其它的錯(cuò)誤就必須由它周?chē)淖饔糜蛱幚恚?/p>

var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
    try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
} 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.")
}
// 打印 “Insufficient funds. Please insert an additional 2 coins.”

上面的例子中,buyFavoriteSnack(person:vendingMachine:) 函數(shù)在一個(gè) try 表達(dá)式中調(diào)用,因?yàn)樗軖伋鲥e(cuò)誤。如果錯(cuò)誤被拋出,相應(yīng)的執(zhí)行會(huì)馬上轉(zhuǎn)移到 catch 子句中,并判斷這個(gè)錯(cuò)誤是否要被繼續(xù)傳遞下去。如果沒(méi)有錯(cuò)誤拋出,do 子句中余下的語(yǔ)句就會(huì)被執(zhí)行。

將錯(cuò)誤轉(zhuǎn)換成可選值

可以使用 try? 通過(guò)將錯(cuò)誤轉(zhuǎn)換成一個(gè)可選值來(lái)處理錯(cuò)誤。如果在評(píng)估 try? 表達(dá)式時(shí)一個(gè)錯(cuò)誤被拋出,那么表達(dá)式的值就是 nil。例如,在下面的代碼中,xy 有著相同的數(shù)值和等價(jià)的含義:

func someThrowingFunction() throws -> Int {
    // ...
}

let x = try? someThrowingFunction()

let y: Int?
do {
    y = try someThrowingFunction()
} catch {
    y = nil
}

如果 someThrowingFunction() 拋出一個(gè)錯(cuò)誤,xy 的值是 nil。否則 xy 的值就是該函數(shù)的返回值。注意,無(wú)論 someThrowingFunction() 的返回值類(lèi)型是什么類(lèi)型,xy 都是這個(gè)類(lèi)型的可選類(lèi)型。例子中此函數(shù)返回一個(gè)整型,所以 xy 是可選整型。

如果你想對(duì)所有的錯(cuò)誤都采用同樣的方式來(lái)處理,用 try? 就可以讓你寫(xiě)出簡(jiǎn)潔的錯(cuò)誤處理代碼。例如,下面的代碼用幾種方式來(lái)獲取數(shù)據(jù),如果所有方式都失敗了則返回 nil。

func fetchData() -> Data? {
    if let data = try? fetchDataFromDisk() { return data }
    if let data = try? fetchDataFromServer() { return data }
    return nil
}

禁用錯(cuò)誤傳遞

有時(shí)你知道某個(gè) throwing 函數(shù)實(shí)際上在運(yùn)行時(shí)是不會(huì)拋出錯(cuò)誤的,在這種情況下,你可以在表達(dá)式前面寫(xiě) try! 來(lái)禁用錯(cuò)誤傳遞,這會(huì)把調(diào)用包裝在一個(gè)不會(huì)有錯(cuò)誤拋出的運(yùn)行時(shí)斷言中。如果真的拋出了錯(cuò)誤,你會(huì)得到一個(gè)運(yùn)行時(shí)錯(cuò)誤。

例如,下面的代碼使用了 loadImage(atPath:) 函數(shù),該函數(shù)從給定的路徑加載圖片資源,如果圖片無(wú)法載入則拋出一個(gè)錯(cuò)誤。在這種情況下,因?yàn)閳D片是和應(yīng)用綁定的,運(yùn)行時(shí)不會(huì)有錯(cuò)誤拋出,所以適合禁用錯(cuò)誤傳遞。

let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")

指定清理操作

你可以使用 defer 語(yǔ)句在即將離開(kāi)當(dāng)前代碼塊時(shí)執(zhí)行一系列語(yǔ)句。該語(yǔ)句讓你能執(zhí)行一些必要的清理工作,不管是以何種方式離開(kāi)當(dāng)前代碼塊的——無(wú)論是由于拋出錯(cuò)誤而離開(kāi),或是由于諸如 returnbreak 的語(yǔ)句。例如,你可以用 defer 語(yǔ)句來(lái)確保文件描述符得以關(guān)閉,以及手動(dòng)分配的內(nèi)存得以釋放。

defer 語(yǔ)句將代碼的執(zhí)行延遲到當(dāng)前的作用域退出之前。該語(yǔ)句由 defer 關(guān)鍵字和要被延遲執(zhí)行的語(yǔ)句組成。延遲執(zhí)行的語(yǔ)句不能包含任何控制轉(zhuǎn)移語(yǔ)句,例如 break、return 語(yǔ)句,或是拋出一個(gè)錯(cuò)誤。延遲執(zhí)行的操作會(huì)按照它們聲明的順序從后往前執(zhí)行——也就是說(shuō),第一條 defer 語(yǔ)句中的代碼最后才執(zhí)行,第二條 defer 語(yǔ)句中的代碼倒數(shù)第二個(gè)執(zhí)行,以此類(lèi)推。最后一條語(yǔ)句會(huì)第一個(gè)執(zhí)行。

func processFile(filename: String) throws {
    if exists(filename) {
        let file = open(filename)
        defer {
            close(file)
        }
        while let line = try file.readline() {
            // 處理文件。
        }
        // close(file) 會(huì)在這里被調(diào)用,即作用域的最后。
    }
}

上面的代碼使用一條 defer 語(yǔ)句來(lái)確保 open(_:) 函數(shù)有一個(gè)相應(yīng)的對(duì) close(_:) 函數(shù)的調(diào)用。

注意

即使沒(méi)有涉及到錯(cuò)誤處理的代碼,你也可以使用 defer 語(yǔ)句。