【Swift 3.1】18 - 錯誤處理 (Error Handling)

【Swift 3.1】18 - 錯誤處理 (Error Handling)

自從蘋果2014年發(fā)布Swift瘩燥,到現(xiàn)在已經(jīng)兩年多了担钮,而Swift也來到了3.1版本檀训。去年利用工作之余妓盲,共花了兩個多月的時間把官方的Swift編程指南看完∨榇猓現(xiàn)在整理一下筆記净赴,回顧一下以前的知識火诸。有需要的同學(xué)可以去看官方文檔>>该窗。


錯誤處理是在程序中響應(yīng)錯誤條件和恢復(fù)錯誤條件的過程。

表示和拋出錯誤 (Representing and Throwing Errors)

在Swift中拌消,錯誤是用遵循了Error協(xié)議的類型的值來表示挑豌。枚舉非常適合用來封裝相關(guān)的錯誤。

enum VendingMachineError: Error {
    case invalidSelection
    case insufficientFunds(coinsNeeded: Int)
    case outOfStock
}

是用throw來拋出錯誤:

throw VendingMachineError.insufficientFunds(coinsNeeded: 5)

處理錯誤 (Handling Errors)

當(dāng)錯誤拋出后墩崩,一些相關(guān)的代碼必須處理錯誤浮毯,例如改正錯誤、嘗試另外一種辦法或者告知用戶有錯誤泰鸡。

在Swift中,有四種方法來處理錯誤壳鹤。把錯誤傳遞給調(diào)用這個方法的代碼盛龄;使用do-catch語句處理錯誤;把錯誤處理為一個可選類型的值芳誓;或者斷言這個錯誤不會發(fā)生余舶。下面會演示這四個方法。

當(dāng)一個方法拋出了錯誤锹淌,它會改變程序的流程匿值,所以及時發(fā)現(xiàn)錯誤的位置非常重要。為了發(fā)現(xiàn)錯誤的位置赂摆,在調(diào)用方法或初始化器的代碼前使用try挟憔、try?或者try!钟些。

使用拋出方法來傳遞錯誤 (Propagating Errors Using Throwing Functions)

為了表示一個方法或初始化器可以拋出異常,在方法參數(shù)后面加上throw關(guān)鍵字:

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

注意:只有拋出方法才能傳遞錯誤绊谭,不能拋出錯誤的方法只能在方法內(nèi)處理錯誤政恍。

下面是一個例子:

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:)方法的實現(xiàn)中,使用guard語句來拋出錯誤达传。

因為vend(itemNamed:)把錯誤傳出去了篙耗,所以調(diào)用這個方法的代碼必須處理錯誤:使用do-catch語句、try?宪赶、try!或者繼續(xù)把錯誤傳出去宗弯。下面演示的是用繼續(xù)把錯誤傳出去的方法來處理:

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)
}

因為vend(itemNamed:)能拋出錯誤,所以能再前面加上try搂妻。

初始化器也能拋出錯誤:

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 {
    try expression
    statements
} catch pattern 1 {
    statements
} catch pattern 2 where condition {
    statements
}

catch后面寫一個樣式來提示什么樣的錯誤能被這個catch語句處理蒙保。如果catch語句沒有樣式,那么這個語句會匹配任何錯誤叽讳,并且綁定這個錯誤作為一個本地常量error追他。

catch語句不必處理每一個可能的錯誤。如果沒有catch語句處理這個錯誤岛蚤,那么這個錯誤將會傳遞給周圍——要么用do-catch語句處理邑狸,要么通過內(nèi)部的拋出方法。例如下面這個例子除了VendingMachineError的所有枚舉值涤妒,但其他錯誤只能由周圍的代碼去處理:

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.")
}
// Prints "Insufficient funds. Please insert an additional 2 coins."
把錯誤轉(zhuǎn)換為可選值 (Converting Error to Optional Values)

使用try?把錯誤轉(zhuǎn)為可選值单雾。在使用try?的語句中,如果有錯誤拋出她紫,那么這個語句的值為nil硅堆。

例如下面這個例子,x和y的值相同:

func someThrowingFunction() throws -> Int {
    // ...
}
 
let x = try? someThrowingFunction()
 
let y: Int?
do {
    y = try someThrowingFunction()
} catch {
    y = nil
}

當(dāng)用同一個方法來處理所有錯誤時贿讹,使用try?能是代碼更簡潔:

func fetchData() -> Data? {
    if let data = try? fetchDataFromDisk() { return data }
    if let data = try? fetchDataFromServer() { return data }
    return nil
}
禁用錯誤傳遞 (Disabling Error Propagation)

有時候我們知道一個能拋出錯誤的方法在運行過程中時間上不會拋出錯誤渐逃。在這種情況下,我們可以在語句前使用try!來禁用錯誤傳遞民褂,并且可以封裝在斷言內(nèi)茄菊,如果真的有錯誤拋出,那么程序報運行時錯誤赊堪。

例如:

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

指定清理操作 (Specifying Cleanup Actions)

使用defer語句在代碼執(zhí)行離開當(dāng)前代碼塊之前執(zhí)行一些語句面殖。不管代碼執(zhí)行如何離開當(dāng)前代碼塊,不管是因為報錯哭廉、return或者break脊僚,defer中的語句都能讓我們做一些必要的清理。例如遵绰,可以使用defer語句來保證文件描述符被關(guān)閉和手動分配的內(nèi)存被釋放辽幌。

defer語句直到當(dāng)前代碼塊退出時才會執(zhí)行增淹。不能包括轉(zhuǎn)移控制語句,例如break或者return舶衬,或者拋出一個錯誤埠通。延遲操作將按照他們定義的順序的反序執(zhí)行,也就是說逛犹,第一個defer語句在第二個defer語句之后執(zhí)行端辱。

func processFile(filename: String) throws {
    if exists(filename) {
        let file = open(filename)
        defer {
            close(file)
        }
        while let line = try file.readline() {
            // Work with the file.
        }
        // close(file) is called here, at the end of the scope.
    }
}

上面這個例子使用defer語句來保證open(_:)方法有對應(yīng)的close(_:)方法。

注意:即使沒有涉及錯誤處理代碼也可以使用defer語句虽画。


第十八部分完舞蔽。下個部分:【Swift 3.1】19 - 類型轉(zhuǎn)換 (Type Casting)


如果有錯誤的地方,歡迎指正码撰!謝謝渗柿!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市脖岛,隨后出現(xiàn)的幾起案子朵栖,更是在濱河造成了極大的恐慌,老刑警劉巖柴梆,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件陨溅,死亡現(xiàn)場離奇詭異,居然都是意外死亡绍在,警方通過查閱死者的電腦和手機门扇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來偿渡,“玉大人臼寄,你說我怎么就攤上這事×锟恚” “怎么了吉拳?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長适揉。 經(jīng)常有香客問我留攒,道長,這世上最難降的妖魔是什么涡扼? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮盟庞,結(jié)果婚禮上吃沪,老公的妹妹穿的比我還像新娘。我一直安慰自己什猖,他們只是感情好票彪,可當(dāng)我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布红淡。 她就那樣靜靜地躺著,像睡著了一般降铸。 火紅的嫁衣襯著肌膚如雪在旱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天推掸,我揣著相機與錄音桶蝎,去河邊找鬼。 笑死谅畅,一個胖子當(dāng)著我的面吹牛登渣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播毡泻,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼胜茧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了仇味?” 一聲冷哼從身側(cè)響起呻顽,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎丹墨,沒想到半個月后廊遍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡带到,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年昧碉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片揽惹。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡被饿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出搪搏,到底是詐尸還是另有隱情狭握,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布疯溺,位于F島的核電站论颅,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏囱嫩。R本人自食惡果不足惜恃疯,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望墨闲。 院中可真熱鬧今妄,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至腾仅,卻和暖如春乒裆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背推励。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工鹤耍, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吹艇。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓惰蜜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親受神。 傳聞我的和親對象是個殘疾皇子抛猖,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容