2014-06-17 2 views
4

포럼과 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 싶습니다.

답변

6

당신이 스위프트의 기능 (또는 방법)를 설계하는 경우에는 오류를 처리하는 적어도 3 개를 선택할 수 있습니다 이 작업은 정기적으로 수행되고 선택적 유형 변수를 반환하는 것을 고려하십시오. 예를 들어, 귀하의 경우 add 메서드는 NSDecimalNumber 대신 NSDecimalNumber?을 반환 할 수 있습니다. 이 경우 귀하의 방법은 잘못 될 수있는 모든 사항을 확인한 다음 해당 상황에서 nil을 반환합니다. 오버플로 및 언더 플로는 nil을 반환하고 다른 모든 경우는 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 : 호출자가이 같은 옵션 NSDecimalNumber을 확인하고 포장을 푸는해야합니다 당신이 잘못된 건에 대한 자세한 정보를 반환하려면 열거 형식을

를 돌려, 당신은 만들 수 있습니다 각 오류에 대한 값을 가진 열거 유형 및 응답을 포함하는 성공에 대한 유형입니다. 예를 들어, 당신은 할 수 :

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

그런 다음 추가

MathWhizResult을 반환하도록 정의 될 것이다 :

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

를 오류의 경우, add.Overflow 또는 .Underflow을 반환합니다. 성공의 경우 addSuccess(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을 만들었습니다. StoryBoard에서 ViewController에서 I 3 개 TextField S (1 피연산자 각각 하나씩, 2 피연산자 및 결과) 내가 + 붙여진 Button 첨가.

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

답변 해 주셔서 감사합니다. 이것은 좋은 해결책 인 것 같습니다. 지금 시도해 볼 것입니다. –

+0

흠, # 1과 # 2에서 nil을 반환하기 전에 (추가 기능에서) 오류를 확인하는 방법은 무엇입니까? - 이것이 내가 오버플로에 대한 수표를 원하지만 서클에서 시작되는 크래시를 포함하지 않는 서클의 시작으로 이끄는 것 같습니다 (스위프트에서). –

+0

오버 플로우는'a + b> maxint' 때 발생합니다. 양쪽에서'b'를 빼면'a> maxint-b'가됩니다. 이 경우'nil '을 리턴하십시오. 그것은 정수형을위한 것이지만 아이디어를 얻습니다. – vacawama

1

음, ObjC API를 사용하고 있습니다. 따라서 ObjC 내부에서 예외를 처리하십시오. 예외를 처리하고 Swift 코드의 값을 반환하는 ObjC 클래스를 작성합니다.

하나의 가능성은 ObjC 클래스로 MathWhiz 쓰기와 inout NSError 매개 변수를 반환하는 것 (즉, 그것을 코어 데이터가하는 방법을, **NSError을) 당신이 복구 가능한 오류를 쳤을 때 적절한 값을 입력합니다. 그런 다음 NSDecimalNumber에서 예외를 먹고 NSError 값으로 변환 할 수 있습니다.

Swift 코드에 NSDecimalNumber 대신 사용하는 Swift 소비를위한 전체 NSDecimalNumber 래퍼를 작성할 수도 있습니다. 아마도이 클래스에 대해 operator + 및 해당 형제를 오버로드 한 다음 예외없이 다양한 가능한 오류를 나타내는 방법을 알아낼 수 있습니다. 함수가 실패 할 경우 선택 사양 유형을

를 반환하고, :

선택 1 :

+0

당신을 감사합니다. 아마도 가장 좋은 방법은 이미 작성한 Objective-C 버전을 사용하고 핵심 데이터를 연구하여 매개 변수를 구현하고 언급 한 방식으로 값을 반환하는 것입니다. Apple은 Swift의 모든 현재 API를 지원한다고 말했지만, Objective-C를 사용하지 않으면 Objective-C API에서 예외를 처리하는 방법을 명확하게했는지 확신 할 수 없습니다 (위의 예는 Swift를 사용하여 Objective-C에서했던 것과 비교해보고, Swift로 전환하는 것이 얼마나 쉬운 지 알고 싶었습니다.) –

+0

그래, 나는 그들이 그 질문에 적절히 대답했는지 확신 할 수 없다. 내 생각 엔 예외를 사용하지 않는 API의 Swift 상당 부분을 보게 될 것입니다. – anisoptera

1

당신이 try와 현재, 스위프트 2.0 지원을 언급 한 바와 같이 2.0 빠른

에 대한 업데이트 대답, throwcatch 개의 키워드

here은 공식 발표입니다.

오류 처리 모델 : Swift 2.0의 새로운 오류 처리 모델은 익숙한 try, throw 및 catch 키워드로 즉시 자연 스럽다고 느낍니다. 무엇보다도 Apple SDK 및 NSError와 완벽하게 작동하도록 설계되었습니다. 사실, NSError는 Swift의 ErrorType을 준수합니다. 은 WWDC 세션에서 Swift의 새로운 기능에 대해 자세히보고 싶습니다. 자세한 내용은 을 참조하십시오.

예컨대

func loadData() throws { } 

func test() { 
    do { 
     try loadData() 
    } catch { 
     print(error) 
    } 
} 
관련 문제