exception-handling - Swift Exceptions 异常处理

标签 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 为我做这件事。

最佳答案

如果你在 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 执行此操作。我在 Xcode 中创建了一个 Single View Application。在 StoryBoardViewController 中,我添加了 3 个 TextField(操作数 1、操作数 2 和结果各一个)和一个 按钮,我标记为+

ViewController.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"
        }
    }
}

MathWhiz.swift

import UIKit

// Declare that we implement NSDecimalNumberBehaviors so that we can handle
// exceptions without them being raised.
class MathWhiz: NSDecimalNumberBehaviors {
    var badException = false

    // Required function of NSDecimalNumberBehaviors protocol
    func roundingMode() -> NSRoundingMode {
        return .RoundPlain
    }

    // Required function of NSDecimalNumberBehaviors protocol
    func scale() -> CShort {
        return CShort(NSDecimalNoScale)
    }

    // Required function of NSDecimalNumberBehaviors protocol
    // Here we process the exceptions
    func exceptionDuringOperation(operation: Selector, error: NSCalculationError, leftOperand: NSDecimalNumber, rightOperand: NSDecimalNumber) -> NSDecimalNumber? {
        var errorstr = ""

        switch(error) {
        case .NoError:
            errorstr = "NoError"
        case .LossOfPrecision:
            errorstr = "LossOfPrecision"
        case .Underflow:
            errorstr = "Underflow"
            badException = true
        case .Overflow:
            errorstr = "Overflow"
            badException = true
        case .DivideByZero:
            errorstr = "DivideByZero"
            badException = true
        }
        println("Exception called for operation \(operation) -> \(errorstr)")

        return nil
    }

    // Add two numbers represented by the strings op1 and op2.  Return nil
    // if a bad exception occurs.
    func add(op1: String, op2: String) -> NSDecimalNumber? {
        let dn1 = NSDecimalNumber(string: op1)
        let dn2 = NSDecimalNumber(string: op2)

        // Init badException to false.  It will be set to true if an
        // overflow, underflow, or divide by zero exception occur.
        badException = false

        // Add the NSDecimalNumbers, passing ourselves as the implementor
        // of the NSDecimalNumbersBehaviors protocol.
        let dn3 = dn1.decimalNumberByAdding(dn2, withBehavior: self)

        // Return nil if a bad exception happened, otherwise return the result
        // of the add.
        return badException ? nil : dn3
    }
}

关于exception-handling - Swift Exceptions 异常处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24269395/

相关文章:

exception-handling - Laravel 异常处理 - 如何使用 API 和 Html 处理异常

java - Spring MVC 返回 JSONS 和异常处理

ios - 核心数据删除所有关系实体

Swift 删除元素字典并重新加载 tableview

ios - 如何删除 SceneKit 相机上的 "Fish Eye"效果?

ios - NumberFormatter 和货币字符串的欧元错误

ios - NSDecimalNumber 占位符泄漏

c# - 检查功能参数的最佳方法 : Check for null or try/catch

c++ - 派生自 std::exception

iphone - 将 NSDecimalNumber 计算为负次方