ErrorHandling(錯誤處理)

// 錯誤處理

//個人理解: 拋出錯誤用throw關鍵字。
// 在有可能拋出錯誤的 函數(shù)聲明里加入throws關鍵字,稱為throw函數(shù)我碟,可以傳遞該錯誤, 不影響沒有錯誤時候返回正常的返回值翅阵。
// 調(diào)用可以拋出錯誤的函數(shù),使用try關鍵字赚抡。

//“錯誤處理(Error handling)是響應錯誤以及從錯誤中恢復的過程。Swift 提供了在運行時對可恢復錯誤的拋出纠屋、捕獲狱掂、傳遞和操作的一等公民支持「破ぃ”
//“可選類型可用來表示值缺失辫愉,但是當某個操作失敗時,最好能得知失敗的原因族铆,從而可以作出相應的應對岩四。”

//1.表示并拋出錯誤
//“Swift 的枚舉類型尤為適合構建一組相關的錯誤狀態(tài)哥攘,枚舉的關聯(lián)值還可以提供錯誤狀態(tài)的額外信息剖煌。例如,你可以這樣表示在一個游戲中操作自動販賣機時可能會出現(xiàn)的錯誤狀態(tài):”

enum VendingMachineError:Error{
    case invalideSelection  //選擇無效
    case insufficientFunds(coinsNeeded:Int) //金額不足
    case outOfStack      //缺貨
}

//“拋出一個錯誤可以讓你表明有意外情況發(fā)生逝淹,導致正常的執(zhí)行流程無法繼續(xù)執(zhí)行耕姊。拋出錯誤使用throw關鍵字”

throw VendingMachineError.insufficientFunds(coinsNeeded: 5)

//2. 處理錯誤
//“Swift 中有4種處理錯誤的方式。你可以把函數(shù)拋出的錯誤傳遞給調(diào)用此函數(shù)的代碼栅葡、用do-catch語句處理錯誤箩做、將錯誤作為可選類型處理、或者斷言此錯誤根本不會發(fā)生”
//“當一個函數(shù)拋出一個錯誤時妥畏,你的程序流程會發(fā)生改變邦邦,所以重要的是你能迅速識別代碼中會拋出錯誤的地方。為了標識出這些地方醉蚁,在 調(diào)用一個能拋出錯誤的函數(shù)燃辖、方法或者構造器之前,加上try關鍵字网棍, 或者try?或try!這種變體”
//“Swift 中的錯誤處理和其他語言中用try黔龟,catch和throw進行異常處理很像。和其他語言中(包括 Objective-C )的異常處理不同的是,Swift 中的錯誤處理并不涉及解除調(diào)用棧氏身,這是一個計算代價高昂的過程巍棱。就此而言,throw語句的性能特性是可以和return語句相媲美的蛋欣『结悖”

//2.1 用throwing函數(shù)傳遞錯誤
//“為了表示一個函數(shù)、方法或構造器可以拋出錯誤陷虎,在 函數(shù)聲明 的參數(shù)列表之后 加上throws關鍵字到踏。一個標有throws關鍵字的函數(shù)被稱作throwing 函數(shù)。如果這個函數(shù)指明了返回值類型尚猿,throws關鍵詞需要寫在箭頭(->)的前面窝稿。”

//func canThrowError(name:String) throws -> String
//func cannotThrowError(name:String) ->String

//“一個 throwing 函數(shù)可以在其內(nèi)部拋出錯誤凿掂,并將錯誤傳遞到函數(shù) 被調(diào)用時的作用域
//“注意 只有 throwing 函數(shù)可以傳遞錯誤伴榔。任何在某個非 throwing 函數(shù)內(nèi)部拋出的錯誤只能在函數(shù)內(nèi)部處理。

struct Item {
    var price:Int
    var count:Int
}

class VendingMachine{
    var inventory = ["candybar":Item(price:12,count:7),
                     "chips":Item(price:10,count:4),
                     "pretzels":Item(price:7,count:11)
                    ]
    var coinsDesposited = 0
    func dispenseSnacek(snack:String){
        print("dispensing\(snack)")
    }
    func vend(itemNamed name:String)throws {
        guard let item = inventory[name] else {
            throw VendingMachineError.invalideSelection
        }
        guard item.count > 0 else {
            throw VendingMachineError.outOfStack
        }
        guard item.price < coinsDesposited else{
            throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDesposited)
        }
        coinsDesposited -= item.price
        
        var newItem = item
        newItem.count -= 1
        inventory[name] = newItem
        print("dispending \(name)")
    }
}

//“在vend(itemNamed:)方法的實現(xiàn)中使用了guard語句來提前退出方法庄萎,確保在購買某個物品所需的條件中踪少,有任一條件不滿足時,能提前退出方法并拋出相應的錯誤惨恭。由于throw語句會立即退出方法,所以物品只有在所有條件都滿足時才會被售出耙旦⊥严郏”
//“因為vend(itemNamed:)方法會傳遞出它拋出的任何錯誤,在你的代碼中調(diào)用此方法的地方免都,必須要么直接處理這些錯誤——使用do-catch語句锉罐,try?或try!;要么繼續(xù)將這些錯誤傳遞下去绕娘。例如下面例子中脓规,buyFavoriteSnack(_:vendingMachine:)同樣是一個 throwing 函數(shù),任何由vend(itemNamed:)方法拋出的錯誤會一直被傳遞到buyFavoriteSnack(person:vendingMachine:)函數(shù)被調(diào)用的地方险领。

let facoriteSnacks = ["Alice":"chips",
                      "Bob":"licorice",
                      "Eve":"pretzels"
                     ]
func buyFavoriteSnack(person:String,vendingMachine:VendingMachine)throws{
    let snakeName = facoriteSnacks[person] ?? "candybar"
    try vendingMachine.vend(itemNamed: snakeName)
}

//“buyFavoriteSnack(person:vendingMachine:)函數(shù)會查找某人最喜歡的零食侨舆,并通過調(diào)用vend(itemNamed:)方法來嘗試為他們購買。因為vend(itemNamed:)方法能拋出錯誤绢陌,所以在調(diào)用的它時候在它前面加了try關鍵字挨下。”

//2.2 用do-catch來處理錯誤
//“可以使用一個do-catch語句運行一段閉包代碼來處理錯誤脐湾。如果在do子句中的代碼拋出了一個錯誤臭笆,這個錯誤會與catch子句做匹配,從而決定哪條子句能處理它〕钇蹋”
//“在catch后面寫一個匹配模式來表明這個子句能處理什么樣的錯誤鹰霍。如果一條catch子句沒有指定匹配模式,那么這條子句可以匹配任何錯誤茵乱,并且把錯誤綁定到一個名字為error的局部常量”
//“catch子句不必將do子句中的代碼所拋出的每一個可能的錯誤都作處理茂洒。如果所有catch子句都未處理錯誤,錯誤就會傳遞到周圍的作用域似将。然而获黔,錯誤還是必須要被某個周圍的作用域處理的——要么是一個外圍的do-catch錯誤處理語句,要么是一個 throwing 函數(shù)的內(nèi)部”

var vendingMachine = VendingMachine()
vendingMachine.coinsDesposited = 8
do {
    try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
} catch VendingMachineError.invalideSelection  {
    print("invalideSelection")
}catch VendingMachineError.outOfStack {
    print("out of stack")
}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ù)在一個try表達式中調(diào)用玷氏,因為它能拋出錯誤。如果錯誤被拋出腋舌,相應的執(zhí)行會馬上轉移到catch子句中盏触,并判斷這個錯誤是否要被繼續(xù)傳遞下去。如果沒有錯誤拋出块饺,do子句中余下的語句就會被執(zhí)行赞辩。

//2.3 將錯誤轉換成可選值
// “可以使用try?通過將錯誤轉換成一個可選值來處理錯誤。如果在評估try?表達式時一個錯誤被拋出授艰,那么表達式的值就是nil,例如,在下面的代碼中,x和y有著相同的數(shù)值和等價的含義

func someThrowingFunction() throws ->Int{
    return Int(2)
}
let  x = try?someThrowingFunction()

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

//“如果someThrowingFunction()拋出一個錯誤辨嗽,x和y的值是nil。否則x和y的值就是該函數(shù)的返回值淮腾。注意糟需,無論someThrowingFunction()的返回值類型是什么類型,x和y都是這個類型的可選類型谷朝。例子中此函數(shù)返回一個整型洲押,所以x和y是可選整型≡不耍”
//“如果你想對所有的錯誤都采用同樣的方式來處理杈帐,用try?就可以讓你寫出簡潔的錯誤處理代碼。例如专钉,下面的代碼用幾種方式來獲取數(shù)據(jù)挑童,如果所有方式都失敗了則返回nil”

func fatchData() -> Data?{
//    if let data = try? fatchDataFromDisk(){return data}
//    if let data = try? fatchDataFromServer(){return data}
    return nil
}

//2.4 禁止錯誤傳遞
// “有時你知道某個throwing函數(shù)實際上在運行時是不會拋出錯誤的,在這種情況下跃须,你可以在表達式前面寫try!來禁用錯誤傳遞炮沐,這會把調(diào)用包裝在一個不會有錯誤拋出的運行時斷言中。如果真的拋出了錯誤回怜,你會得到一個運行時錯誤大年』槐。”

//3. 指定清理操作
//“可以使用defer語句在 即將離開當前代碼塊時執(zhí)行一系列語句 。該語句讓你能執(zhí)行一些必要的清理工作翔试,不管是以何種方式離開當前代碼塊的——無論是由于拋出錯誤而離開轻要,還是由于諸如return或者break的語句”
//“defer語句將代碼的執(zhí)行延遲到當前的作用域退出之前。該語句由defer關鍵字和要被延遲執(zhí)行的語句組成垦缅。延遲執(zhí)行的語句不能包含任何控制轉移語句冲泥,例如break或是return語句,或是拋出一個錯誤壁涎。延遲執(zhí)行的操作會按照它們被指定時的順序的相反順序執(zhí)行——也就是說凡恍,第一條defer語句中的代碼會在第二條defer語句中的代碼被執(zhí)行之后才執(zhí)行,以此類推”

//func processFile(fileName:String)throws{
//
//    if exists(fileName) {
//        let file = open(fileName)
//        defer {
//            close(file)
//        }
//        while let line = try file.readline() {
//            //處理文件
//        }
//        // close(file) 會在這里被調(diào)用怔球,即作用域的最后
//    }
//}
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嚼酝,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子竟坛,更是在濱河造成了極大的恐慌闽巩,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件担汤,死亡現(xiàn)場離奇詭異涎跨,居然都是意外死亡,警方通過查閱死者的電腦和手機崭歧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門隅很,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人率碾,你說我怎么就攤上這事叔营。” “怎么了播掷?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵审编,是天一觀的道長撼班。 經(jīng)常有香客問我歧匈,道長,這世上最難降的妖魔是什么砰嘁? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任件炉,我火速辦了婚禮,結果婚禮上矮湘,老公的妹妹穿的比我還像新娘斟冕。我一直安慰自己,他們只是感情好缅阳,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布磕蛇。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪秀撇。 梳的紋絲不亂的頭發(fā)上超棺,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機與錄音呵燕,去河邊找鬼棠绘。 笑死,一個胖子當著我的面吹牛再扭,可吹牛的內(nèi)容都是我干的氧苍。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼泛范,長吁一口氣:“原來是場噩夢啊……” “哼让虐!你這毒婦竟也來了?” 一聲冷哼從身側響起敦跌,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤澄干,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后柠傍,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體麸俘,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年惧笛,在試婚紗的時候發(fā)現(xiàn)自己被綠了从媚。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡患整,死狀恐怖拜效,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情各谚,我是刑警寧澤紧憾,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站昌渤,受9級特大地震影響赴穗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜膀息,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一般眉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧潜支,春花似錦甸赃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽络断。三九已至,卻和暖如春项玛,著一層夾襖步出監(jiān)牢的瞬間妓羊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工稍计, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留躁绸,地道東北人。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓臣嚣,卻偏偏與公主長得像净刮,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子硅则,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

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