快速异常异常处理 [英] Swift Exceptions to Exception handling

查看:152
本文介绍了快速异常异常处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通过论坛和Swift文档(不完全承认),通过Swift文档,似乎不用使用try-catch机制,在Swift中,我们鼓励编写比异常更安全的代码。有鉴于此,我有一个关于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:返回可选类型



如果您的功能可能会失败,并且定期发生,然后考虑返回一个可选的类型变量。例如,在您的情况下,您的方法添加可能会返回一个 NSDecimalNumber?而不是一个简单的 NSDecimalNumber 。在这种情况下,您的方法将检查可能出错的所有内容,并在这些情况下返回 nil 。溢出和下溢将返回 nil ,所有其他情况都会返回一个 NSDecimalNumber 。呼叫者将必须检查并解开可选的NSDecimalNumber,如下所示:

  let brain = MathWhiz()
if let sum = brain.add(1,op2:1e127){
println(result is \(sum))
} else
println(错误的MathWhiz添加)
}

选择2:返回枚举类型



如果要返回更多有关出错问题的信息,可以为每个错误创建一个带有值的枚举类型,一个用于嵌入答案的成功。例如,您可以执行以下操作:

 枚举MathWhizResult {
案例溢出
案例下溢
case成功(NSDecimalNumber)
}

然后添加将被定义为返回 MathWhizResult

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

如果出现错误,添加 .Overflow .Underflow 。在成功的情况下,添加将返回 Success(result)。调用者必须检查枚举并解压缩结果。可以使用开关

  switch(brain.add (1,op2:1e128)){
case .Overflow
println(darn,it overflowed)
case .Underflow
println )
case .Success(let answer)
println(结果是\(答案)
}

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



在前两个选项中解开结果可能太多开销对于一个非常罕见的错误,您可以选择只返回一个结果,并让调用者处理下溢或溢出情况的可能性,在这种情况下,他们必须在调用 add 。它的好处是,他们知道他们的程序永远不会导致下溢或溢出(因为他们正在处理单个数字的数字),他们不负责打包结果。






我创建了一个小应用程序来演示如何使用 NSDecimalNumbers 来实现。我在 Xcode 中创建了一个单一查看应用程序。在 ViewController StoryBoard 中添加了3个 TextField (操作数1,操作数2和结果一个)和我标记为 + Button p>

ViewController.swift

  import UIKit 

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

var brain = MathWhiz()

覆盖func viewDidLoad(){
super.viewDidLoad()
//在加载后视图,通常从一个笔尖。
}

覆盖func didReceiveMemoryWarning(){
super.didReceiveMemoryWarning()
//处理可以重新创建的任何资源。
}

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

//使用操作数字段的内容执行add操作。
//打印答案,如果添加返回nil,则显示无结果。
如果让answer = brain.add(op1,op2:op2)?描述{
result.text = answer
} else {
result.text =No Result
}
}
}

MathWhiz.swift

  import UIKit 

//声明我们实现NSDecimalNumberBehaviors,以便我们可以处理
//没有被提出的异常。
class MathWhiz:NSDecimalNumberBehaviors {
var badException = false

// NSDecimalNumberBehaviors协议的必需函数
func roundingMode() - > NSRoundingMode {
return .RoundPlain
}

// NSDecimalNumberBehaviors协议的必需功能
func scale() - > CShort {
return CShort(NSDecimalNoScale)
}

// NSDecimalNumberBehaviors协议的必需函数
//这里我们处理异常
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(异常调用操作\(操作) - > \(errorstr))

return nil
}

//添加由字符串op1和op2表示的两个数字。返回nil
//如果发生错误的异常。
func add(op1:String,op2:String) - > NSDecimalNumber? {
let dn1 = NSDecimalNumber(string:op1)
let dn2 = NSDecimalNumber(string:op2)

// Init badException为false。如果
//溢出,下溢或除以零异常,它将被设置为true。
badException = false

//添加NSDecimalNumbers,将其作为NSDecimalNumbersBehaviors协议的实现者
//传递。
let dn3 = dn1.decimalNumberByAdding(dn2,withBehavior:self)

//如果发生不正常的异常返回nil,否则返回结果
//的add。
返回badException? nil:dn3
}
}


After perusing through forums and Swift documentation (not completely, I admit), it appears that instead of try-catch mechanisms, in Swift we are encouraged to write code that is more safe from exceptions. In light of that, I have a question about a sample API, and would like to learn how to more safely handle this situation:

For example, I can create the following class using the NSDecimalNumberHandler:

class MathWhiz {

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

If I use the following, I get a number:

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

However, if I cause an overflow exception,:

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

I will crash the program as expected.

So, my question is, the API raises exceptions, but I don't handle them here. There are other posts out there of people pointing out that Swift does not have exception handling, but this question is seeking a nice way to handle this problem in the way that the language creators were thinking it should be done. Is there a recommended way to handle this without having to write my own code to check for overflow, underflow, loss of precision, ect...? I am wanting the NSDecimalNumberHandler to do that for me.

解决方案

If you are designing a function (or method) in Swift, you have at least 3 choices for dealing with errors:

Choice 1: Return an Optional Type

If your function might fail, and this happens on a regular basis, then consider returning an optional type variable. For example, in your case, your method add could return an NSDecimalNumber? instead of a plain NSDecimalNumber. In that case, your method would check for everything that could go wrong, and return nil in those situations. Overflow and underflow would return nil, and all other cases would return an NSDecimalNumber. The callers would have to check for and unwrap the optional NSDecimalNumber like this:

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

Choice 2: Return an Enumerated Type

If you want to return more information about the thing that went wrong, you could create an enumerated type with a value for each error and one for success that embeds the answer. For example, you could do:

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

Then add would be defined to return MathWhizResult:

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

In the case of an error, add would return .Overflow or .Underflow. In the case of success, add would return Success(result). The caller would have to check the enumeration and unpack the result. A switch could be used for this:

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

Choice 3: Choose not to handle errors explicitly

Unpacking the result in the first two choices might be too much overhead for an error that is very rare. You could chose to just return a result, and let the caller deal with the possibility of an underflow or overflow condition. In that case, they would have to check for these conditions themselves before calling add. The benefit is, it they know that their program will never cause an underflow or overflow (because they are dealing with single digit numbers for instance), they are not burdened with unpacking the result.


I created a small app to demonstrate how you could do this with NSDecimalNumbers. I created a Single View Application in Xcode. In the ViewController in the StoryBoard I added 3 TextFields (one each for operand 1, operand 2, and the result) and a Button that I labelled +.

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

这篇关于快速异常异常处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆