异常处理的Swift异常

时间:2014-06-17 16:54:37

标签: exception-handling swift nsdecimalnumber

通过浏览论坛和Swift文档(不完全,我承认)后,似乎在Swift中我们鼓励编写更安全的代码而不是try-catch机制。鉴于此,我对一个示例API有疑问,并想学习如何更安全地处理这种情况:

例如,我可以使用NSDecimalNumberHandler创建以下类:

class MathWhiz {

    init() {
    let defaultBehavior: NSDecimalNumberHandler =
    NSDecimalNumberHandler.defaultDecimalNumberHandler()
    }
    func add(op1: String, op2: String) ->NSDecimalNumber {
        return NSDecimalNumber.decimalNumberWithString(op1).decimalNumberByAdding(NSDecimalNumber.decimalNumberWithString(op2))
    }
}

如果我使用以下内容,我会得到一个号码:

let brain = MathWhiz()
brain.add("1", op2: "1e127")

但是,如果我导致溢出异常,:

brain.add("1", op2: "1e128")

我会按预期崩溃程序。

所以,我的问题是,API会引发异常,但我不会在这里处理它们。还有其他帖子指出Swift没有异常处理,但是这个问题正在寻找一种很好的方式来处理这个问题,就像语言创建者认为应该完成的那样。 有没有一种推荐的方法来处理这个问题而不必编写我自己的代码来检查溢出,下溢,精度损失等等?我想要NSDecimalNumberHandler为我做这件事。

3 个答案:

答案 0 :(得分:6)

如果您正在设计Swift中的函数(或方法),则至少有3种选择来处理错误:

选择1:返回可选类型

如果您的函数可能会失败,并且这种情况会定期发生,那么请考虑返回一个可选的类型变量。例如,在您的情况下,您的方法add可能会返回NSDecimalNumber?而不是普通NSDecimalNumber。在这种情况下,您的方法将检查可能出错的所有内容,并在这些情况下返回nil。溢出和下溢将返回nil,所有其他情况将返回NSDecimalNumber。调用者必须检查和解包可选的NSDecimalNumber,如下所示:

let brain = MathWhiz()
if let sum = brain.add("1", op2: "1e127") {
    println("the result was \(sum)")
} else
    println("something went wrong with MathWhiz add")
}

选择2:返回枚举类型

如果要返回有关出错的更多信息,可以创建一个枚举类型,其中包含每个错误的值,另一个用于嵌入答案的成功。例如,你可以这样做:

enum MathWhizResult {
    case Overflow
    case Underflow
    case Success(NSDecimalNumber)
}

然后将定义add以返回MathWhizResult

func add(op1: String, op2: String) -> MathWhizResult

如果出现错误,add将返回.Overflow.Underflow。如果成功,add将返回Success(result)。调用者必须检查枚举并解压缩结果。可以使用switch

switch (brain.add("1", op2: "1e128")) {
case .Overflow
    println("darn, it overflowed")
case .Underflow
    println("underflow condition happened")
case .Success(let answer)
    println("the result was \(answer)"
}

选择3:选择不明确处理错误

在前两个选项中解压缩结果对于非常罕见的错误可能是过多的开销。您可以选择仅返回结果,并让调用者处理下溢或溢出情况的可能性。在这种情况下,他们必须在调用add之前自己检查这些条件。好处是,他们知道他们的程序永远不会导致下溢或溢出(因为他们正在处理单个数字的数字),他们没有负担解压缩结果。


我创建了一个小应用来演示如何使用NSDecimalNumbers执行此操作。我在Single View Application中创建了Xcode。在ViewController的{​​{1}}中,我添加了3 StoryBoard s(操作数1,操作数2和结果各一个)和TextField我标记为Button }}

<强> ViewController.swift

+

<强> MathWhiz.swift

import UIKit

class ViewController: UIViewController {
    @IBOutlet var operand1 : UITextField!
    @IBOutlet var operand2 : UITextField!
    @IBOutlet var result   : UITextField!

    var brain = MathWhiz()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func addButton(sender : UIButton) {
        var op1 = operand1.text
        var op2 = operand2.text

        // Perform the add with the contents of the operand fields.
        // Print the answer, or "No Result" if add returns nil.
        if let answer = brain.add(op1, op2: op2)?.description {
            result.text = answer
        } else {
            result.text = "No Result"
        }
    }
}

答案 1 :(得分:1)

好吧,你正在使用ObjC API。所以只需处理ObjC中的异常。编写一个ObjC类来处理异常并返回要使用的Swift代码的值。

一种可能性是将MathWhiz写为ObjC类并返回inout NSError参数(即按照Core Data的方式执行,取**NSError)并填充它遇到可恢复的错误时的正确值。然后,您可以从NSDecimalNumber中获取异常并将其转换为NSError值。

您还可以为Swift消费编写一个完整的NSDecimalNumber包装器,然后在Swift代码中使用它来代替NSDecimalNumber。也许你可以为这个类重载operator +及其兄弟姐妹,然后找出如何在没有例外的情况下表示各种可能的错误。

答案 2 :(得分:1)

更新swift 2.0的答案

正如您所提到的,现在Swift 2.0支持trythrowcatch个关键字。

here是正式公告。

  

错误处理模型:Swift 2.0中的新错误处理模型将会   通过熟悉的尝试,抛出和捕获关键字,立即感觉自然。   最重要的是,它的设计与Apple SDK和Apple SDK完美配合   NSError。实际上,NSError符合Swift的ErrorType。你会   肯定想观看WWDC上关于Swift的新内容的会议   了解更多信息。

例如

func loadData() throws { }

func test() {
    do {
        try loadData()
    } catch {
        print(error)
    }
}