Exception handling 异常处理的Swift异常
在仔细阅读了论坛和Swift文档之后(我承认这并不完全),我们似乎不想尝试捕获机制,而是鼓励在Swift中编写更安全的代码,以避免异常。有鉴于此,我有一个关于示例API的问题,希望了解如何更安全地处理这种情况: 例如,我可以使用NSDecimalNumberHandler创建以下类:Exception handling 异常处理的Swift异常,exception-handling,swift,nsdecimalnumber,Exception Handling,Swift,Nsdecimalnumber,在仔细阅读了论坛和Swift文档之后(我承认这并不完全),我们似乎不想尝试捕获机制,而是鼓励在Swift中编写更安全的代码,以避免异常。有鉴于此,我有一个关于示例API的问题,希望了解如何更安全地处理这种情况: 例如,我可以使用NSDecimalNumberHandler创建以下类: class MathWhiz { init() { let defaultBehavior: NSDecimalNumberHandler = 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能帮我做到这一点。好吧,您使用的是ObjC API。所以只需处理ObjC内部的异常。编写一个ObjC类,该类处理异常并返回值供Swift代码使用 一种可能是将
MathWhiz
编写为一个ObjC类,并返回一个inout-NSError
参数(即按照核心数据的方式执行,获取一个**NSError
),并在遇到可恢复错误时用适当的值填充它。然后,您可以读取NSDecimalNumber
中的异常,并将其转换为NSError
值
您还可以为Swift消费编写一个完整的
NSDecimalNumber
包装器,然后用它代替Swift代码中的NSDecimalNumber
。也许您可以重载该类的运算符+
及其同级,然后确定如何毫无例外地表示各种可能的错误。如果您正在用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 (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
中创建了一个单视图应用程序。在情节提要的视图控制器中,我添加了3个文本字段
s(操作数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"
}
}
}
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
}
}
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"
}
}
}
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
}
}
更新swift 2.0的答案
正如您所提到的,现在Swift 2.0支持try
,throw
,以及catch
关键字
这是官方公告
错误处理模型:Swift 2.0中的新错误处理模型将
使用熟悉的try、Trow和catch关键字,立即感觉自然。
最棒的是,它被设计成可以完美地与苹果SDK和iPhone配合使用
错误。事实上,N错误符合Swift的错误类型。你会
肯定想看一下WWDC会议,了解Swift to的最新内容
多听听
e、 g
非常感谢。也许最好的方法是使用我已经编写的Objective-C版本,并研究核心数据,以实现您提到的参数和返回值。苹果表示,他们支持Swift中所有当前的API,但我不确定他们是否明确表示,如果我们不使用objective-C,我们应该如何处理objective-C API中的异常(我上面的例子是一个使用Swift的练习,看看它与我在Objective-C中所做的相比如何,我想看看我能多么容易地将我的工作转换为Swift).是的,我不确定他们是否充分回答了这个问题。我的猜测是,我们将看到那些不使用异常的API的Swift等价物。谢谢你的回答;这似乎是一个不错的解决方案。我现在就去试试。嗯,在#1和#2中,在返回nil之前如何检查错误(在add函数中)?--似乎是