Swift 錯(cuò)誤處理和調(diào)試詳解

1.表示和拋出錯(cuò)誤

在Swift中速种,錯(cuò)誤由符合Error協(xié)議的類型的值表示。這個(gè)空協(xié)議表示類型可以用于錯(cuò)誤處理低千。
Swift枚舉特別適合于對(duì)一組相關(guān)錯(cuò)誤條件進(jìn)行建模配阵,還可以給相關(guān)值添加錯(cuò)誤性質(zhì)的附加信息。

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

使用關(guān)鍵字throw表示示血,待執(zhí)行語(yǔ)句拋出的錯(cuò)誤信息棋傍。

throw VendingMachineError.insufficientFunds(coinsNeeded: 5)

2.處理錯(cuò)誤

(1).使用拋出函數(shù)傳播錯(cuò)誤

  • 使用關(guān)鍵字throws表示函數(shù),方法难审,或構(gòu)造器可以拋出錯(cuò)誤瘫拣;
  • throws添加在函數(shù)聲明的參數(shù)后面,返回箭頭->之前剔宪;
  • 拋出函數(shù)在執(zhí)行是拂铡,將其中拋出的錯(cuò)誤傳播到調(diào)用它的范圍壹无;
func canThrowErrors() throws -> String

注意:只有拋出函數(shù)才能傳播錯(cuò)誤。非throwing函數(shù)拋出的任何錯(cuò)誤感帅,必須在函數(shù)內(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:方法內(nèi)部并沒(méi)有處理錯(cuò)誤,而是直接將拋出的錯(cuò)誤傳播出去失球。其他調(diào)用該方法的代碼必需使用do-catch語(yǔ)句岖是,try?try!实苞,或繼續(xù)傳播錯(cuò)誤等方式處理這些錯(cuò)誤豺撑。

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:)也是
一個(gè)拋出函數(shù),它內(nèi)部調(diào)用的vend(itemNamed:方法會(huì)拋出錯(cuò)誤黔牵,因此在方法執(zhí)行前面添加關(guān)鍵字try來(lái)調(diào)用聪轿。

如拋出函數(shù)一樣,拋出構(gòu)造器也可以按同樣的方式傳播錯(cuò)誤猾浦。

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

(2).使用 Do-Catch處理錯(cuò)誤
通過(guò)運(yùn)行代碼塊陆错,可以使用do-catch語(yǔ)句來(lái)處理錯(cuò)誤。如果do閉包中的代碼拋出了錯(cuò)誤金赦,它將與catch閉包匹配音瓷,以確定哪一個(gè)可以處理錯(cuò)誤。

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

可以在catch之后編寫要處理的錯(cuò)誤模式夹抗,以指示閉包可以處理哪些錯(cuò)誤绳慎。如果catch閉包沒(méi)有相匹配的錯(cuò)誤模式,則將錯(cuò)誤交給最后的catch閉包來(lái)處理漠烧,并將錯(cuò)誤綁定到名為error的本地常量中杏愤。

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).")
}
// Prints "Insufficient funds. Please insert an additional 2 coins."

在上面的例子中,buyFavoriteSnack(person:vendingMachine:)函數(shù)在try表達(dá)式中調(diào)用沽甥,因?yàn)樗麜?huì)拋出錯(cuò)誤声邦。當(dāng)有錯(cuò)誤拋出時(shí),就立刻轉(zhuǎn)到相應(yīng)的catch閉包中處理錯(cuò)誤摆舟。若沒(méi)有錯(cuò)誤亥曹,則繼續(xù)執(zhí)行do閉包中的剩下語(yǔ)句。

這些catch閉包不必處理do閉包中的代碼可能拋出的所有錯(cuò)誤恨诱。如果沒(méi)有catch閉包處理錯(cuò)誤媳瞪,則錯(cuò)誤將傳播到周圍的范圍。但是照宝,傳播的錯(cuò)誤必須由周圍的某個(gè)范圍處理蛇受。在非拋出函數(shù)中,包含do-catch的閉包必須處理錯(cuò)誤厕鹃。在拋出函數(shù)中兢仰,要么包含do-catch閉包乍丈,要么調(diào)用方必須處理錯(cuò)誤。如果錯(cuò)誤傳播到頂級(jí)范圍而不被處理把将,將出現(xiàn)運(yùn)行時(shí)錯(cuò)誤轻专。

func nourish(with item: String) throws {
    do {
        try vendingMachine.vend(itemNamed: item)
    } catch is VendingMachineError {
        print("Invalid selection, out of stock, or not enough money.")
    }
}

do {
    try nourish(with: "Beet-Flavored Chips")
} catch {
    print("Unexpected non-vending-machine-related error: \(error)")
}
// Prints "Invalid selection, out of stock, or not enough money."

(3).將錯(cuò)誤轉(zhuǎn)換為可選值

可以使用try?通過(guò)將錯(cuò)誤轉(zhuǎn)換為可選值來(lái)處理錯(cuò)誤。如果在執(zhí)行try?表達(dá)式時(shí)拋出錯(cuò)誤察蹲,則表達(dá)式的值將為nil请垛。

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

let x = try? someThrowingFunction()

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

當(dāng)你想用同樣的方式來(lái)處理所有的錯(cuò)誤時(shí),使用try?可以讓你寫出簡(jiǎn)潔的錯(cuò)誤處理代碼洽议。
若下例所示宗收,如果所有方法都失敗,則返回nil:

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

(4).禁用錯(cuò)誤傳播
有時(shí)你知道拋出函數(shù)或方法不會(huì)在運(yùn)行時(shí)拋出錯(cuò)誤亚兄。在這種情況下混稽,你可以在表達(dá)式前面添加try!來(lái)禁止錯(cuò)誤的傳播,并將調(diào)用包裝在運(yùn)行時(shí)斷言中儿捧,斷言不會(huì)拋出錯(cuò)誤荚坞。
如果實(shí)際拋出了一個(gè)錯(cuò)誤挑宠,將得到一個(gè)運(yùn)行時(shí)錯(cuò)誤菲盾。

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

3.指定清理操作

  • defer語(yǔ)句所在的代碼塊,無(wú)論是以何種方式結(jié)束執(zhí)行(如拋出錯(cuò)誤各淀,break或return語(yǔ)句)懒鉴, defer中的代碼始終都會(huì)被執(zhí)行,且最后執(zhí)行碎浇;
  • 當(dāng)多個(gè)defer語(yǔ)句在同一代碼塊中時(shí)临谱,它被執(zhí)行的順序與其在源代碼中寫入的順序相反(即源代碼順序中的最后一個(gè)defer語(yǔ)句首先執(zhí)行);
    例如奴璃,使用defer語(yǔ)句來(lái)確保關(guān)閉了文件描述符并釋放手動(dòng)分配的內(nèi)存悉默。
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.
    }
}

4.斷言和先決條件

  • 斷言和先決條件是在運(yùn)行時(shí)發(fā)生的檢查。
  • 如果斷言或先決條件中的布爾值為true苟穆,則代碼將繼續(xù)正常執(zhí)行抄课。如果為false,則程序的當(dāng)前狀態(tài)無(wú)效; 代碼執(zhí)行結(jié)束雳旅,應(yīng)用程序終止跟磨。
  • 斷言可以在開(kāi)發(fā)過(guò)程中發(fā)現(xiàn)錯(cuò)誤和錯(cuò)誤的假設(shè);先決條件可以檢測(cè)生產(chǎn)中的問(wèn)題攒盈。
  • 斷言和先決條件與上述錯(cuò)誤處理中討論的錯(cuò)誤條件不同抵拘,它們不用于可恢復(fù)的或預(yù)期的錯(cuò)誤。因?yàn)槭〉臄嘌曰蛳葲Q條件表示無(wú)效的程序狀態(tài)型豁,所以無(wú)法捕獲失敗的斷言僵蛛。
  • 使用斷言和先決條件執(zhí)行有效的數(shù)據(jù)和狀態(tài)尚蝌,可以使應(yīng)用程序在出現(xiàn)無(wú)效狀態(tài)時(shí)更容易終止,易于調(diào)試問(wèn)題充尉。
  • 斷言和先決條件之間的區(qū)別在于何時(shí)檢測(cè)它們:斷言只在調(diào)試版本中檢查驼壶,而先決條件在調(diào)試和生產(chǎn)版本中都檢查。在生產(chǎn)版本中喉酌,不進(jìn)行評(píng)估斷言中的條件热凹。這意味著可以在開(kāi)發(fā)過(guò)程中使用任意數(shù)量的斷言,而不會(huì)影響生產(chǎn)中的性能泪电。

(1).使用斷言調(diào)試
從Swift標(biāo)準(zhǔn)庫(kù)中調(diào)用assert(_:_:file:line:)函數(shù)來(lái)編寫斷言般妙。向此函數(shù)傳遞給一個(gè)表達(dá)式,該表達(dá)式求值為truefalse相速。如果條件的結(jié)果為false碟渺,則顯示一條消息。
例如:

let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// This assertion fails because -3 is not >= 0.

可以省略斷言消息 - 例如:

assert(age >= 0)

若代碼已經(jīng)檢查了條件突诬,則使用assertionFailure(_:file:line:)函數(shù)來(lái)指明斷言已失敗苫拍。例如:

if age > 10 {
    print("You can ride the roller-coaster or the ferris wheel.")
} else if age > 0 {
    print("You can ride the ferris wheel.")
} else {
    assertionFailure("A person's age can't be less than zero.")
}

(2).執(zhí)行先決條件
當(dāng)條件可能為假時(shí),但是為了能夠使代碼繼續(xù)執(zhí)行旺隙,條件必須為真绒极,則使用先決條件。
可以通過(guò)調(diào)用precondition(_:_:file:line:)函數(shù)來(lái)編寫先決條件蔬捷。向該函數(shù)傳遞一個(gè)計(jì)算結(jié)果為真或假的表達(dá)式垄提,并在條件結(jié)果為假時(shí)顯示一條消息。例如:

// In the implementation of a subscript...
precondition(index > 0, "Index must be greater than zero.")

還可以調(diào)用preconditionFailure(_:file:line:)函數(shù)來(lái)指明失敗發(fā)生時(shí)——例如周拐,如果選擇了開(kāi)關(guān)的默認(rèn)情況铡俐,但是所有有效的輸入數(shù)據(jù)應(yīng)該由開(kāi)關(guān)的其中一種情況處理。

5.致命錯(cuò)誤

  • 無(wú)條件輸出給定的錯(cuò)誤消息并停止執(zhí)行妥粟。
//message: 要打印的字符串
//file: 輸出“message”的文件名
//line:輸出“message”的行號(hào)
public func fatalError(_ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line) -> Never

6.其他專題模塊

Swift 4.2 基礎(chǔ)專題詳解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末审丘,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子勾给,更是在濱河造成了極大的恐慌滩报,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锦秒,死亡現(xiàn)場(chǎng)離奇詭異露泊,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)旅择,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門惭笑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事沉噩∞嘧冢” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵川蒙,是天一觀的道長(zhǎng)蚜厉。 經(jīng)常有香客問(wèn)我,道長(zhǎng)畜眨,這世上最難降的妖魔是什么昼牛? 我笑而不...
    開(kāi)封第一講書人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮康聂,結(jié)果婚禮上贰健,老公的妹妹穿的比我還像新娘。我一直安慰自己恬汁,他們只是感情好伶椿,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著氓侧,像睡著了一般脊另。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上约巷,一...
    開(kāi)封第一講書人閱讀 52,682評(píng)論 1 312
  • 那天偎痛,我揣著相機(jī)與錄音,去河邊找鬼载庭。 笑死看彼,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的囚聚。 我是一名探鬼主播,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼标锄,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼顽铸!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起料皇,我...
    開(kāi)封第一講書人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤谓松,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后践剂,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體鬼譬,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年逊脯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了优质。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖巩螃,靈堂內(nèi)的尸體忽然破棺而出演怎,到底是詐尸還是另有隱情,我是刑警寧澤避乏,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布爷耀,位于F島的核電站,受9級(jí)特大地震影響拍皮,放射性物質(zhì)發(fā)生泄漏歹叮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一铆帽、第九天 我趴在偏房一處隱蔽的房頂上張望盗胀。 院中可真熱鬧,春花似錦锄贼、人聲如沸票灰。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)屑迂。三九已至,卻和暖如春冯键,著一層夾襖步出監(jiān)牢的瞬間惹盼,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工惫确, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留手报,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓改化,卻偏偏與公主長(zhǎng)得像掩蛤,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子陈肛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

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

  • ??由于 JavaScript 本身是動(dòng)態(tài)語(yǔ)言句旱,而且多年來(lái)一直沒(méi)有固定的開(kāi)發(fā)工具阳藻,因此人們普遍認(rèn)為它是一種最難于調(diào)...
    霜天曉閱讀 763評(píng)論 0 1
  • 本章將會(huì)介紹 自動(dòng)引用計(jì)數(shù)的工作機(jī)制自動(dòng)引用計(jì)數(shù)實(shí)踐類實(shí)例之間的循環(huán)強(qiáng)引用解決實(shí)例之間的循環(huán)強(qiáng)引用閉包引起的循環(huán)強(qiáng)...
    寒橋閱讀 911評(píng)論 0 0
  • 第2章 基本語(yǔ)法 2.1 概述 基本句法和變量 語(yǔ)句 JavaScript程序的執(zhí)行單位為行(line)啃匿,也就是一...
    悟名先生閱讀 4,153評(píng)論 0 13
  • 初夏將致 頂著火紅的烈日 迎著曬脫皮的臉頰 也要在強(qiáng)光下苦練車技 只為早日拿到車牌 一路風(fēng)光無(wú)限 蟬鳴低唱 云霧縈...
    婉_霞閱讀 514評(píng)論 7 19
  • 新春的鐘聲已經(jīng)敲響蛔外,新年的祝福送到四面八方蛆楞。 外面鞭炮聲聲,預(yù)示新的一年紅紅火火蒸蒸日上冒萄。 過(guò)年是團(tuán)圓的日子臊岸,一家...
    萬(wàn)兒閱讀 248評(píng)論 0 3