Swift do-try-catch语法

我试着去理解swift 2中的新的error handling事情。下面是我所做的:我首先声明了一个错误枚举:

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可以理解,每个case处理都是详尽无遗的。

Swift 2error handling模型有两个重点:穷举和弹性。 他们一起归结为你的do / catch声明需要抓住每一个可能的错误,而不仅仅是你可以抛出的错误。

请注意,您不会声明函数可以抛出什么types的错误,而只是抛出它是否抛出。 这是一个无穷无尽的问题:作为为其他人定义function(包括未来自己)的人,您不希望让function的每个客户都适应您实施中的每一个变化函数,包括可能抛出的错误。 你希望调用你的函数的代码能够适应这种变化。

因为你的函数不能说出它会抛出什么样的错误(或者可能抛出未来),所以catch错误的catch块不知道它会抛出什么types的错误。 所以,除了处理你所知道的错误types之外,你还需要处理那些你没有使用通用catch语句的types – 这样,如果你的函数改变了将来抛出的错误集,调用者仍然会捕获其错误。

 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) } 

但是,我们不要止步于此。 再想一想这个韧性理念。 你devise你的三明治的方式,你必须在每个地方使用它们来描述错误。 这意味着,无论何时你改变一组错误的情况下,你必须改变每一个使用它们的地方…不是很有趣。

定义自己的错误types的想法是让你集中这样的事情。 你可以为你的错误定义一个description方法:

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

然后,您的error handling代码可以请求您的错误types来描述自己 – 现在,您处理错误的每个地方都可以使用相同的代码,并处理将来可能出现的错误情况。

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

这也为错误types(或其扩展)铺平了道路,以支持其他报告错误的方式 – 例如,您可以在知道如何向iOS用户报告错误的UIAlertController展示错误types的扩展。

我怀疑这只是尚未正确实施。 Swift编程指南似乎暗示编译器可以推断穷举匹配“就像switch语句”一样。 它没有提到需要一个普遍的catch ,以便穷尽。

你也会注意到错误是在try线上,而不是在块的结尾,也就是说,在某个时刻,编译器将能够确定块中哪个try语句有未处理的exceptiontypes。

文档虽然有点模糊。 我已经浏览了“Swift中的新function”video,找不到任何线索。 我会继续尝试。

更新:

我们现在已经达到了Beta 3,并没有提示ErrorType推断。 我现在相信,如果这是有计划的(我仍然认为这是在某种程度上),协议扩展的dynamic调度可能会杀死它。

Beta 4更新:

Xcode 7b4添加了对Throws: doc注释支持Throws: “应该用来logging可以抛出什么错误以及为什么抛出错误”。 我想这至less提供了一些机制来向API消费者传达错误。 当你有文档时谁需要一个types系统?

另一个更新:

花了一些时间希望自动的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 

另一个更新

苹果的error handling理由现在可以在这里find 。 在迅速发展的邮件列表上也有一些有趣的讨论。 从本质上讲,John McCall反对input错误,因为他相信大多数库最终都会包含一个通用的错误情况,并且除了样板文件(他使用术语“愿望虚张声势”)之外,types错误不太可能会增加代码。 克里斯·莱特纳(Chris Lattner)表示,如果能够在弹性模型中工作,他可以​​在Swift 3中input错误。

Swift担心你的case语句不能覆盖所有的情况,要修复它,你需要创build一个默认的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") } 

我也对缺less函数可以抛出的types感到失望,但是现在我要感谢@rickster,我将这样总结:让我们说我们可以指定一个函数抛出的types,我们可以像这样:

 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的函数的地方