快速 do-try-catch 语法

我尝试了解新的错误处理迅速2的东西。下面是我所做的: 我首先声明了一个错误枚举:

enum SandwichError: ErrorType {
case NotMe
case DoItYourself
}

然后我声明了一个抛出错误的方法(不是异常)。这是一个错误。).方法如下:

func makeMeSandwich(names: [String: String]) throws -> String {
guard let sandwich = names["sandwich"] else {
throw SandwichError.NotMe
}


return sandwich
}

问题出在调用方。下面是调用此方法的代码:

let kitchen = ["sandwich": "ready", "breakfeast": "not ready"]


do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
print("Not me error")
} catch SandwichError.DoItYourself {
print("do it error")
}

do行编译器说 Errors thrown from here are not handled because the enclosing catch is not exhaustive之后。但在我看来,这是详尽的,因为只有两个情况下,在 SandwichError枚举。

对于经常切换语句迅速可以理解它是详尽的时候,每个案件处理。

187251 次浏览

Swift 担心你的 case 语句没有涵盖所有的情况,为了解决这个问题,你需要创建一个默认的 case:

do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
print("Not me error")
} catch SandwichError.DoItYourself {
print("do it error")
} catch Default {
print("Another Error")
}

Swift 2错误处理模型有两个重点: 详尽性和弹性。总之,它们归结为 do/catch语句需要捕获每个可能的错误,而不仅仅是那些您知道可以抛出的错误。

请注意,您没有声明函数可以抛出哪些类型的错误,而只是声明它是否会抛出错误。这是一个0-1-无穷大类型的问题: 作为一个为他人(包括你未来的自己)定义函数的人,你不想让你函数的每个客户端都适应你函数实现中的每个变化,包括它可能抛出的错误。您希望调用函数的代码能够适应这种变化。

因为函数不能说明它抛出了什么类型的错误(或者将来可能抛出什么类型的错误) ,所以捕获它的 catch块不知道它可能抛出什么类型的错误。因此,除了处理已知的错误类型之外,还需要使用通用的 catch语句来处理不知道的错误类型——这样,如果函数改变了将来抛出的错误集,调用方仍然会捕获它的错误。

do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
print("Not me error")
} catch SandwichError.DoItYourself {
print("do it error")
} catch let error {
print(error.localizedDescription)
}

但我们不能就此止步。再多想想这个关于恢复力的想法。按照你设计三明治的方式,你必须在使用它们的每个地方描述错误。这意味着,无论何时更改错误用例集,都必须更改使用它们的每个位置... ... 这不是很有趣。

定义自己的错误类型背后的思想是让您集中处理这样的事情。您可以为您的错误定义一个 description方法:

extension SandwichError: CustomStringConvertible {
var description: String {
switch self {
case NotMe: return "Not me error"
case DoItYourself: return "Try sudo"
}
}
}

然后您的错误处理代码可以要求您的错误类型来描述它自己——现在您处理错误的每个地方都可以使用相同的代码,并且还可以处理未来可能出现的错误情况。

do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch let error as SandwichError {
print(error.description)
} catch {
print("i dunno")
}

这也为错误类型(或错误类型上的扩展)支持其他报告错误的方式铺平了道路——例如,您可以在错误类型上安装一个扩展,它知道如何为向 iOS 用户报告错误提供 UIAlertController

我怀疑这只是还没有得到正确的实施。快速编程指南似乎明确地暗示编译器可以推断出详尽的匹配“就像 switch 语句”。它没有提到需要一个通用的 catch为了是详尽的。

您还会注意到,错误出现在 try行,而不是代码块的末尾,也就是说,在某个时刻,编译器将能够确定代码块中哪个 try语句具有未处理的异常类型。

不过,文档有点模棱两可。我浏览了一下“斯威夫特有什么新鲜事”的视频,找不到任何线索; 我会继续尝试。

更新:

我们现在进入 Beta 3,没有任何 ErrorType 推断的提示。我现在相信,如果这曾经是计划好的(我仍然认为它在某个时候是计划好的) ,协议扩展的克劳斯·福尔曼可能会扼杀它。

Beta 4更新:

Xcode 7b4添加了对 Throws:的 doc 注释支持,它“应该用于记录可能抛出的错误以及抛出错误的原因”。我想这至少提供了 一些机制来将错误传递给 API 使用者。当你有文档的时候,谁还需要类型系统!

另一个更新:

在花了一些时间希望自动 ErrorType推理,并找出该模型的局限性之后,我改变了主意——我希望苹果实现的是 这个。基本上:

// allow us to do this:
func myFunction() throws -> Int


// or this:
func myFunction() throws CustomError -> Int


// but not this:
func myFunction() throws CustomErrorOne, CustomErrorTwo -> Int

又一个更新

苹果的错误处理原理现在可以使用 给你。在 快速进化邮件列表中也有一些有趣的讨论。从本质上讲,John McCall 反对输入错误,因为他相信大多数库最终都会包含一个通用的错误案例,而且输入错误除了样板代码之外不太可能给代码增加太多内容(他使用了术语“渴望虚张声势”)。Chris Lattner 表示,如果 Swift 3能够与弹性模型协同工作,他对输入错误持开放态度。

我也对函数没有抛出的类型感到失望,但是我现在明白了,多亏了@rickster,我把它总结成这样: 假设我们可以指定函数抛出的类型,我们会有这样的东西:

enum MyError: ErrorType { case ErrorA, ErrorB }


func myFunctionThatThrows() throws MyError { ...throw .ErrorA...throw .ErrorB... }


do {
try myFunctionThatThrows()
}
case .ErrorA { ... }
case .ErrorB { ... }

问题是,即使我们不修改 myFunctionThatThrows 中的任何内容,如果我们只是向 MyError 添加一个错误情况:

enum MyError: ErrorType { case ErrorA, ErrorB, ErrorC }

我们完蛋了,因为我们的 do/try/catch 不再是穷举的,还有我们调用抛出 MyError 的函数的任何其他地方

创建枚举如下:

//Error Handling in swift
enum spendingError : Error{
case minus
case limit
}

创建如下方法:

 func calculateSpending(morningSpending:Double,eveningSpending:Double) throws ->Double{
if morningSpending < 0 || eveningSpending < 0{
throw spendingError.minus
}
if (morningSpending + eveningSpending) > 100{
throw spendingError.limit
}
return morningSpending + eveningSpending
}

现在检查错误是否存在并处理它:

do{
try calculateSpending(morningSpending: 60, eveningSpending: 50)
} catch spendingError.minus{
print("This is not possible...")
} catch spendingError.limit{
print("Limit reached...")
}
enum NumberError: Error {
case NegativeNumber(number: Int)
case ZeroNumber
case OddNumber(number: Int)
}


extension NumberError: CustomStringConvertible {
var description: String {
switch self {
case .NegativeNumber(let number):
return "Negative number \(number) is Passed."
case .OddNumber(let number):
return "Odd number \(number) is Passed."
case .ZeroNumber:
return "Zero is Passed."
}
}
}


func validateEvenNumber(_ number: Int) throws ->Int {
if number == 0 {
throw NumberError.ZeroNumber
} else if number < 0 {
throw NumberError.NegativeNumber(number: number)
} else if number % 2 == 1 {
throw NumberError.OddNumber(number: number)
}
return number
}

现在验证号码:

 do {
let number = try validateEvenNumber(0)
print("Valid Even Number: \(number)")
} catch let error as NumberError {
print(error.description)
}

错误可以处理使用开关情况下捕获

func  checkAge(age:Int) throws {


guard !(age>0 && age < 18) else{
throw Adult.child
}


guard !(age >= 60) else{
throw Adult.old
}
    

guard (age>0) else{
throw Adult.notExist
}
    

}




do{
try checkAge(age:0)
      

}
catch let error {
switch error{
case Adult.child : print("child")
case Adult.old : print("old")
case Adult.notExist : print("not Exist")
default:
print("default")
}
}


enum Adult:Error {
case child
case old
case notExist
}