/**錯誤處理:是響應錯誤以及從錯誤中恢復的過程。
Swift提供了在運行時對可恢復錯誤的拋出、捕獲、傳遞和操作的支持。
*/
//表示并拋出錯誤
//在Swift 中 錯誤用符合Error協(xié)議的類型的值來表示。這個空協(xié)議表明該類型可以用于錯誤處理。
//枚舉類型非常適合構建一組相關的錯誤狀態(tài),枚舉的關聯(lián)值還可以提供錯誤狀態(tài)的額外信息,例如:
enum VendingMachineError: Error {//表示一個游戲中操作自動販賣機可能會出現(xiàn)的錯誤狀態(tài)
case invalidSelection //選擇無效
case insufficientFunds(coinsNeeded: Int) //金額不足
case outOfStock //缺貨
}
//拋出錯誤使用 throw 關鍵字。
//throw VendingMachineError.insufficientFunds(coinsNeeded: 5) // 拋出一個錯誤提示:販賣機還需要5個硬幣
//處理錯誤
//當錯誤被拋出是,附近的代碼必須負責處理這個錯誤:糾正、嘗試另一種方式、向用戶報告錯誤等。
//為了快速識別代碼中拋出錯誤的地方,在調用一個能拋出錯誤的函數、方法或構造器之前,加上try關鍵字,或者try?、try!這種變體。
//Swift有四種處理錯誤的方式:把錯誤傳遞給調用此函數的代碼、用do-catch語句處理錯誤、將錯誤作為可選類型處理、斷言此錯誤根本不會發(fā)生。
//1.用 throwing 函數傳遞錯誤
//為了表示一個函數、方法或構造器可以拋出錯誤,在函數聲明的參數列表之后加上 throws 關鍵字、如果函數指明了返回值類型,throw關鍵字需要寫在箭頭(->)的前面;一個標有 throws 關鍵字的函數被稱為 throwing 函數
func canThrowErrors() throws -> String {
return "可以拋出錯誤的函數"
}
func cannotThrowErrors() -> String {
return "不能拋出錯誤的函數"
}
//注意:throwing 函數在其內部拋出錯誤,并將錯誤傳遞到函數被調用時的作用域;其他函數內部拋出的錯誤都只能在函數內部處理,不能傳遞出去。
struct ItemTestError {
var price: Int
var count: Int
}
class VendingMachine {
var inventory = ["棒棒糖": ItemTestError(price: 1, count: 3),
"榨菜": ItemTestError(price: 9, count: 4),
"酸奶": ItemTestError(price: 13, count: 2)]
var coinsDeposited = 0 //投入的硬幣
func dispenseSnack(snack: String) {
print("分配:\(snack)")
}
func vend(itemNamed name: String) throws {
//以下使用了三條 guard 語句來提取退出方法,確保在購買中任一條件不滿足是,能提前退出方法并拋出相應的錯誤
guard let item = inventory[name] else { // 如果選擇的商品不在 庫存中
throw VendingMachineError.invalidSelection // 拋出錯誤: 選擇無效
}
guard item.count > 0 else { // 如果選擇的商品 不大于0
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("購買2:\(name)")
}
}
let favoriteSnacks = ["Alice": "榨菜",
"Bob": "酸奶",
"Eve": "棒棒糖"]
func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {//查找默認最喜歡的零食
let snackName = favoriteSnacks[person] ?? "Candy Bar"
try vendingMachine.vend(itemNamed: snackName) //調用該方法,來嘗試購買;因為該方法可能會拋出錯誤,所以調用時前面加上了try
}
//throwing構造器能像throwing函數一樣傳遞錯誤。
struct PurchasedSnack {//PurchasedSnack構造器在構造過程中調用了throwing函數,并且通過傳遞到它的調用者來處理這些錯誤。
let name: String
init(name: String, vendingMachine: VendingMachine) throws {
try vendingMachine.vend(itemNamed: name)
self.name = name
}
}
//用 Do-Catch處理錯誤
//do中拋出錯誤,catch中匹配錯誤,匹配成功者做相應的處理;如果catch中沒有指定匹配模式,那么久可以匹配任何錯誤,并把錯誤綁定到一個名字為error的局部常量。
//若do中拋出的錯誤,沒有被catch匹配處理,那么就會由他周圍的作用域處理:要么是一個外圍的do-catch錯誤處理語句,要么是一個throwing函數的內部。
var vendingMachineDo = VendingMachine()
vendingMachineDo.coinsDeposited = 10
do { //do 中拋出錯誤
try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachineDo)
print("如果上一句代碼拋出了錯誤,就會跳到下方的catch中執(zhí)行,如果沒有拋出錯誤,這條語句才會被打印")
} catch VendingMachineError.invalidSelection {//拋出錯誤: 選擇無效
print("選擇無效")
} catch VendingMachineError.outOfStock { //拋出錯誤: 缺貨
print("缺貨")
} catch VendingMachineError.insufficientFunds(let coinsNeeded) { //拋出錯誤:投入的硬幣不夠
print("投入的硬幣不夠尚缺少:\(coinsNeeded)枚")
}
//將錯誤轉換成可選值
/*可以使用try?通過將錯誤轉換成一個可選值來處理錯誤。如果在評估try?表達式時一個錯誤被拋出,那么表達式的值就是nil。
func someKeFunction() throws -> Int {//如果拋出一個錯誤,那么x和y的值都是nil。否則x和y的值就是該函數的返回值。
// ...
}
let x = try? someKeFunction()
let y: Int?
do {
y = try someKeFunction()
} catch {
y = nil
}
*/
//如果你想對所有的錯誤都采用同樣的方式來處理,用try?就可以讓你寫出簡潔的錯誤處理代碼。
func fetchData() -> Int? {//如果下面的代碼來獲取數據都失敗了,則返回nil
if let data = try? fetchDataFromDisk() { return data }
if let data = try? fetchDataFromServer() { return data }
return nil
}
func fetchDataFromDisk() throws -> Int {
return 1
}
func fetchDataFromServer() throws -> Int {
return 2
}
//禁用錯誤傳遞
//有時你知道某個throwing函數實際上在運行時是不會拋出錯誤的,在這種情況下,你可以在表達式前面寫try!來禁用錯誤傳遞,這回把調用包裝在一個不會有錯誤拋出的運行時斷言中。如果真的拋出了錯誤,你會得到一個運行時錯誤。
//let photo = try! loadImage(atPath:"./Resources/John Appleseed.jpg")
//上面這種情況下,因為圖片是和應用綁定的,運行時不會有錯誤拋出,所以適合禁用錯誤傳遞。
/**指定清理操作
可以使用defer語句在即將離開當前代碼塊時執(zhí)行一系列語句。
無論是由于拋出錯誤而離開,或是由于諸如return、break的語句而離開代碼塊,都可以用defer語句來做以前清理工作,比如:確保文件描述符得以關閉、釋放手動分配的內存等。
defer語句由defer關鍵字和要被延遲到作用域最后執(zhí)行的語句組成,延遲執(zhí)行的語句不能包含 break語句、return語句 或 拋出一個錯誤。
如果有多個defer語句,會按聲明的順序倒序執(zhí)行,即先聲明的最后執(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) 會在這里被調用,即作用域的最后
}
}
//即使沒有涉及到錯誤處理,也可以使用defer語句
*/
21.Swift錯誤處理
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
相關閱讀更多精彩內容
- |Swift|C++:-:|:-:|:-:關鍵字或類型|Error, throws, try, do - catc...
- 在Swift的標準庫,很多方法名后都帶有'throws'這個關鍵詞, 'throws'表示該方法在執(zhí)行過程中遇到錯...
- 本章將會介紹 自動引用計數的工作機制自動引用計數實踐類實例之間的循環(huán)強引用解決實例之間的循環(huán)強引用閉包引起的循環(huán)強...
- 1. 本節(jié)課將為你解析異常捕捉語句,該語句主要用于對異常和錯誤進行監(jiān)測和處理。同時通過一個食品出售的實例,演示異常...