• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

swift学习-20--错误处理

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

// 错误处理 是响应错误以及 从错误中恢复的过程, Swift 提供了在运行时对 可恢复错误的 抛出, 捕获, 传递 和 操作的支持

 

// 某些操作无法保证总是执行完所有代码 或总是生层有用结果, 可选类型 可用来表示值缺失, 但是当某个操作失败时, 最好能得知失败的原因, 从而可以做出相应的应对

 

// Swift 中的错误处理涉及到错误处理模式, 这会用到 Cocoa 和 Object-C 中的 NSError, 

 

 

 

 

// 表示并抛出错误

// 在 Swift 中, 错误用符合 Error 协议的类型的值来表示, 这个空协议表明该类型可以用于错误处理

 

// Swift 的枚举类型尤为适合构建一组相关的错误状态, 枚举的关联还可以提供错误状态的额外信息, 例如, 你可以这样表示在一个游戏中操作自动贩卖机时可能会出现的错误状态

 

enum VendingmachineError: Error{

    case invalidSelection                     // 选择无效

    case insufficientFunds(coinsNeeded: Int)  // 金额不足

    case outOfStock                           // 缺货

}

 

 

// 抛出一个错误 可以让你表明有意外发生, 导致正常的执行流程无法继续执行, 抛出错误 使用 throw 关键字, 

throw VendingmachineError.insufficientFunds(coinsNeeded: 5)

 

 

 

 

// 处理错误

// 某个错误被抛出时, 附近的某部分代码必须负责处理这个错误, 

 

// Swift 中有 4 种处理错误的方法, 你可以把函数抛出的错误传递给调用此函数的代码, 用 do-catch 语句处理错误, 将错误作为可选类型处理, 或者断言此错误根本不会发生

 

// 当一个函数抛出一个错误时, 你的程序流程会发生变化, 所以重要的是你能迅速识别代码中会抛出 错误的地方, 为了标识出这些地方, 在调用一个能抛出错误的函数, 方法 或者 构造器之前. 加上 try 关键字, 或者 try? 或 try! 这种变体

 

// Swift 中的错误处理和 其他语言中用 try ,catch 和 throw 进行处理很像, 和其他语言中的异常处理不同的是, Swift 中的错误处理并不涉及解除调用栈, 这是一个计算代价高昂的过程, 就此而言 throw 的性能是可以和 return 相媲美的

 

 

 

 

// 用 throwing 函数传递错误

// 为了表示一个函数. 方法 或 构造器可以抛出错误, 在函数的声明 的参数列表之后加上 throw 关键字, 一个标有 throw 关键字的函数被称为 throwing 函数, 如果这个函数指明了返回值类型, throws 关键字需要写在箭头 (->) 的前面

 

func candddd() throws{

    

}

 

func canThrowErrors() throws -> String{

    return "212"

}

 

// 一个 throwing 函数可以在其内部 抛出错误, 并将错误传递到函数被调用的作用域

 

// 注意 : 只有 throwing 函数可以传递错误, 任何非 throwing 函数内部抛出的错误只能在函数内部处理

 

// 下面的例子中,VendingMachine类有一个vend(itemNamed:)方法,如果请求的物品不存在、缺货或者投入金额小于物品价格,该方法就会抛出一个相应的VendingMachineError:

 

struct Item{

    var price: Int

    var count: Int

}

 

class VendingMachine{

    var inventory = [

        "Candy Bar":Item.init(price: 12, count: 7),

        "Chips":Item.init(price: 10, count: 4),

        "Pretezls":Item.init(price: 7, count: 11)

        ]

    

    var conisDeposited = 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 <= conisDeposited else {

            throw VendingmachineError.insufficientFunds(coinsNeeded: item.price - conisDeposited)

        }

        

        conisDeposited -= item.price

        

        var newItem = item

        newItem.count -= 1

        inventory[name] = newItem

        

        print("Dispensing \(name)")

        

    }

}

 

 

// vend(itemNamed:) 方法的实现中使用 guard 语句来提前退出方法, 确保在购买某个物品所需的条件中, 有任一条件不满足时. 能提前退出方法并抛出相应的错误, 由于 throw 语句会立即退出方法, 所以物品只有在所有条件都满足时才会被售出

 

// 因为 vend(itemName:) 方法会传递出它抛出的任何错误, 在你的代码中调用此方法的地方, 必须要么直接处理这些错误 -- do-catch 语句, try 或 try!, 要么继续讲这些错误传递下去

 

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:) 函数会查找某人喜欢的零食, 并通过调用 vend(itemNamed:) 方法来尝试为他们 购买, 因为 vend(itemNamed:) 方法能抛出错误, 所以在调用的时候在它前面加了 try 关键字

 

// throwing 构造器能像 throwing 函数一样传递错误, 例如下面的

struct PurchasedSnack{

    let name: String

    init(name: String, vendingMachine: VendingMachine) throws {

        try vendingMachine.vend(itemNamed: name)

        self.name = name

    }

    

}

 

 

 

 

// 用 Do-Catch 处理错误

// 可以使用一个 do-catch 语句运行一段代码闭包来处理错误, 如果在 do 子句中的代码抛出了一个错误, 这个错误会与 catch 子句做匹配, 从而决定那条子句能处理它

 

// 在 catch 后面写一个 匹配模式 来表明这个句子能处理什么样的错误, 如果一条 catch 子句没有指定任何匹配模式, 那么这条子句可以匹配任何错误, 并且把错误 绑定到一个名字 为 error 的局部常量, 

 

// catch 子句补习将 do 子句中的代码所抛出的每一个错误都做处理, 如果所有的 catch 就都未做处理, 错误 就会传递到周围的作用域, 然而, 错误还是必须要被某个周围作用域处理的, 要么是一个外围的 do-catch 错误处理语句, 要么是他的一个 throwiong 函数的内部, 

 

var vendingMachine = VendingMachine.init()

vendingMachine.conisDeposited = 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("Insuffcient funds. Please insert an additional 2 coins")

}

 

// 上面的例子中, buyFavoriteSnack(person:vendingMachine:) 函数在一个 try 表达式中调用, 因为他能抛出错误, 如果错误被抛出, 相应的执行会马上转移到 catch 子句中, 并判断这个错误是否要被继续传递下去, 如果没有错误, do 子句中余下的语句就会被执行

 

 

 

 

 

 

// 将错误转换成可选值

// 可以使用 try? 通过将错误 转换成一个可选值来处理错误,如果在评估 try? 表达式时一个错误 被抛出, 那么表达式的值就是 nil , 

 

func someThrowingFunction() throws -> Int {

    return 2

}

 

let x = try? someThrowingFunction()

 

 

let y: Int?

do {

    y = try someThrowingFunction()

} catch {

    y = nil

}

 

// 如果 someThrowingFunction() 抛出一个错误, x 和 y 的值是 nil, 否则 x 和 y 的值就是该函数的返回值, 注意 : someThrowingFunction() 的返回值类型是什么类型, x 和 y 都是这个类型的可选类型, 

 

// 如果你想对所有的错误都采用同样的方式来处理, 用 try? 就可以让你写出 简洁的错误处理代码

 

//func fetchData() -> Data? {

//    if let data = try? fetchDataFromDisk() { return data }

//    if let data = try? fetchDataFromServer() { return data }

//    return nil

//}

 

 

 

 

 

// 禁用错误传递

// 有时候你知道某个 throwing 函数实际上在运行时 是不会抛出错误的, 在这种情况下, 你可以在表达式前面写 try! 来禁用错误传递, z这会把调用包装在一个不会有错误抛出的运行时断言中, 如果真的抛出错误, 你会得到一个运行时错误

 

// 例如,下面的代码使用了loadImage(atPath:)函数,该函数从给定的路径加载图片资源,如果图片无法载入则抛出一个错误。在这种情况下,因为图片是和应用绑定的,运行时不会有错误抛出,所以适合禁用错误传递:

 

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

 

 

 

 

 

// 指定清理操作

// 可以使用 defer 语句在即将离开当前代码块时执行一系列语句, 该语句让你能执行一些 必要的清理工作, 不管是以何种方式离开当前代码块的 -- 无论是由于抛出错误离开, 还是由于注入 return 或者 break 的语句

 

// defer 语句将代码的执行 延迟到当前的作用域 退出之前, 该语句有 defer 关键字和 要被延迟的语句组成, 延迟执行的语句不能包含任何控制转移语句, 例如 break 或是 return 语句, 或是 抛出一个错误, 延迟执行的操作会 按照他们被指定时的顺序的 相反顺序执行 -- 也就是说, 第一条 defer 语句中的代码会在 第二条 defer 语句中的代码 被执行之后才执行

 

 

 

//func processFile(filename: String) throws {

//    if exists(filename) {

//        let file = open(filename)

//        defer {

//            close(file)

//        }

//        while let line = try file.readline() {

//            // 处理文件。

//        }

//        // close(file) 会在这里被调用,即作用域的最后。

//    }

//}

// 上面的代码使用一条defer语句来确保open(_:)函数有一个相应的对close(_:)函数的调用。

 

 

// 注意 : 即使没有涉及到错误处理, 你也可以使用 defer 语句


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
[Swift]LeetCode814.二叉树剪枝|BinaryTreePruning发布时间:2022-07-14
下一篇:
[Swift]LeetCode58.最后一个单词的长度|LengthofLastWord发布时间:2022-07-14
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap