// 错误处理 是响应错误以及 从错误中恢复的过程, 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 语句

swift 学习- 20 -- 错误处理的更多相关文章

  1. Swift学习笔记(2):错误处理

    目录: Error do-catch 断言 Error 在 Swift 中,错误用符合 Error 协议的类型的值来表示.这个空协议表明该类型可以用于错误处理异常. Swift 的枚举类型尤为适合构建 ...

  2. swift学习笔记之-错误处理

    //错误处理 import UIKit /*错误处理(Error Handling):响应错误以及从错误中恢复的过程 在 Swift 中,错误用符合ErrorType协议的类型的值来表示.这个空协议表 ...

  3. Swift学习目录

    本学习基于苹果官方Swift学习材料,保留了原版90%左右的内容(一些项目开发中基本不用的知识点没有整理),并根据理解进行整理.如对原版感兴趣,可以直接单击链接阅读和学习. 第一部分 基础篇 1.基本 ...

  4. Swift学习之常用UI的使用

    Swift学习之常用UI的使用 最近笔者在开始学习苹果最新的编程语言,因为笔者认为,苹果既然出了这门语言就绝对不会放弃,除非苹果倒闭了(当然这里知识一个玩笑). 所以在不久的将来,swift绝对是iO ...

  5. swift学习 - 计时器

    swift学习之计时器 这个demo主要学习在swift中如何操作计时器(Timer),按钮(UIButton),文本(Label) 效果图: 代码 import UIKit class ViewCo ...

  6. swift学习第二天:swift中的基本数据类型

    一:swift基本数据类型 Swift中的数据类型也有:整型/浮点型/对象类型/结构体类型等等 先了解整型和浮点型 整型 有符号 Int8 : 有符号8位整型 Int16 : 有符号16位整型 Int ...

  7. 窥探Swift编程之错误处理与异常抛出

    在Swift 2.0版本中,Swift语言对其错误处理进行了新的设计,当然了,重新设计后的结果使得该错误处理系统用起来更爽.今天博客的主题就是系统的搞一下Swift中的错误处理,以及看一下Swift中 ...

  8. 【swift学习笔记】二.页面转跳数据回传

    上一篇我们介绍了页面转跳:[swift学习笔记]一.页面转跳的条件判断和传值 这一篇说一下如何把数据回传回父页面,如下图所示,这个例子很简单,只是把传过去的数据加上了"回传"两个字 ...

  9. 今天开始Swift学习

    今天开始Swift学习  在此记录笔记  以备之后查阅! allenhuang

随机推荐

  1. python3.x与2.x区别

    1.性能 Py3.0运行 pystone benchmark的速度比Py2.5慢30%.Guido认为Py3.0有极大的优化空间,在字符串和整形操作上可 以取得很好的优化结果. Py3.1性能比Py2 ...

  2. Bootstrap 使用

    bootstrap模板为使IE6.7.8版本(IE9以下版本)浏览器兼容html5新增的标签,引入下面代码文件即可. <script src="https://oss.maxcdn.c ...

  3. Ubuntu中安装NVIDIA显卡驱动

    1.参考: https://blog.csdn.net/xunan003/article/details/81665835 https://www.cnblogs.com/luofeel/p/8654 ...

  4. Linux 文件删除 提示 Operation not permitted

     Linux  删除 隐藏文件提示 Operation not permitted ? linux  删除 隐藏文件 提示  Operation not permitted  不允许操作? 使用 ls ...

  5. HTML字体自动换行第二行缩进一格

    p {text-indent: -2em; margin-left: 2em;}设置text-indent: -2em;以后p标签中第一行文字向左偏移,这样第二行开始的文字就等于缩进了,但是这样设置会 ...

  6. Swiper.js使用教程

    官网地址:(http://www.swiper.com.cn/). 一.Swiper.js简介: Swiper(前称Swiper master) 是一款免费以及轻量级的移动设备触控滑块的js框架,使用 ...

  7. python中的join.set ,copy以及删除注意事项:

    1 ,join  :  将对象以字符串的方式拼接成一个整体 for E :  li = ["李白", "是", "诗仙"]    s = & ...

  8. 虚拟节点操作——DocumentFragment

    文章中转站: DocumentFragment对象 createDocumentFragment()用法总结 深入理解DOM节点类型第四篇——文档片段节点DocumentFragment

  9. linux atoi

    atoi 只能针对字符串为数字, 对字符串为十六进制.八进制的不能进行转化

  10. 【mmall】Guava库学习Collections

    参考链接 Guava库学习:学习Collections(三)Sets