Swift do-try-catch语法

时间:2015-06-08 23:05:19

标签: swift swift2

我尝试了解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可以理解在处理每个案例时它都是详尽无遗的。

6 个答案:

答案 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...")
}