我尝试了解swift 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
枚举只有两个案例。
对于常规的switch语句,swift可以理解在处理每个案例时它都是详尽无遗的。
答案 0 :(得分:245)
Swift 2错误处理模型有两个重点:详尽无遗和弹性。总之,它们归结为您需要捕获每个可能错误的do
/ catch
语句,而不仅仅是您知道可以抛出的错误。
请注意,您不会声明函数可以抛出哪些类型的错误,只是它是否会抛出。它是一个零无限的问题:当某人为其他人(包括你未来的自我)定义一个功能时,你不想让你的每个客户都适应每一个变化在你的函数的实现中,包括它可以抛出的错误。您希望调用函数的代码能够适应这种变化。
因为您的功能无法说明它会抛出什么样的错误(或者将来可能抛出的错误),所以发现错误的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")
}
这也为错误类型(或它们的扩展)铺平了道路,以支持其他报告错误的方式 - 例如,您可以在错误类型上有一个扩展,知道如何呈现UIAlertController
向iOS用户报告错误。
答案 1 :(得分:26)
我怀疑这还没有正确实施。 Swift Programming Guide肯定意味着编译器可以推断穷举匹配'就像switch语句一样。它没有提及需要一般catch
以便详尽无遗。
您还会注意到错误发生在try
行,而不是块的结尾,即在某些时候编译器将能够确定哪个try
语句block有未处理的异常类型。
虽然文档有点含糊不清。我已经浏览了“Swift中的新内容”视频,但找不到任何线索;我会继续努力。
<强>更新强>
我们现在已经达到Beta 3,没有提示ErrorType推断。我现在相信,如果这是有计划的(我仍然认为它在某个时候),协议扩展的动态调度可能会将其扼杀。
Beta 4更新:
Xcode 7b4为Throws:
添加了文档评论支持,“应该用于记录可以抛出的错误以及原因”。我想这至少提供了一些机制来向API消费者传达错误。当你有文件时谁需要一个类型系统!
另一次更新:
在花了一些时间希望进行自动ErrorType
推理,并弄清楚该模型的局限性之后,我改变了主意 - this是我希望Apple实现的。基本上:
// 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
又一次更新
现在可以使用Apple的错误处理原理here。关于swift-evolution邮件列表也有一些有趣的讨论。从本质上讲,John McCall反对输入错误,因为他认为大多数库最终都会包含一般错误情况,并且除了样板之外,类型错误不太可能增加代码(他使用的术语&#39; aspirational bluff& #39)。 Chris Lattner表示,如果可以使用弹性模型,他可以在Swift 3中输入错误。
答案 2 :(得分:4)
Swift担心您的案例陈述并未涵盖所有案例,要修复它您需要创建一个默认案例:
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")
}
答案 3 :(得分: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的函数的任何其他地方
答案 4 :(得分:0)
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)
}
答案 5 :(得分:-2)
像这样创建枚举:
//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...")
}