신속한 do-catch 구문


162

나는 신속한 2에서 새로운 오류 처리 문제를 이해하려고 시도합니다. 여기 내가 한 일이 있습니다.

enum SandwichError: ErrorType {
    case NotMe
    case DoItYourself
}

그런 다음 오류를 발생시키는 메소드를 선언했습니다 (예외 사람들이 아닙니다. 오류입니다). 그 방법은 다음과 같습니다.

func makeMeSandwich(names: [String: String]) throws -> String {
    guard let sandwich = names["sandwich"] else {
        throw SandwichError.NotMe
    }

    return sandwich
}

문제는 발신 측에서 발생합니다. 이 메소드를 호출하는 코드는 다음과 같습니다.

let kitchen = ["sandwich": "ready", "breakfeast": "not ready"]

do {
    let sandwich = try makeMeSandwich(kitchen)
    print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
    print("Not me error")
} catch SandwichError.DoItYourself {
    print("do it error")
}

do라인 컴파일러는 말한다 Errors thrown from here are not handled because the enclosing catch is not exhaustive. 그러나 내 의견으로는 SandwichError열거 형 에는 두 가지 사례 만 있기 때문에 철저 합니다.

스위프트는 정기적 인 스위치 설명을 위해 모든 경우를 처리 할 때 철저하다는 것을 이해할 수 있습니다.


3
발생하는 오류 유형을 지정하지 않으므로 Swift가 가능한 모든 옵션을 결정할 수 없습니다.
Farlei Heinen

오류 유형을 지정하는 방법이 있습니까?
mustafa

새 버전의 Swift 책에서 아무 것도 찾을 수 없습니다-지금은 키워드 만 던집니다
Farlei Heinen

오류나 경고없이 놀이터에서 저에게 효과적입니다.
Fogmeister 2016 년

2
놀이터 do는 최상위 수준의 블록이 전체가 아닌 것을 허용하는 것으로 보입니다. 던지지 않는 기능으로 작업을 래핑하면 오류가 발생합니다.
Sam

답변:


267

Swift 2 오류 처리 모델에는 두 가지 중요한 점, 즉 철저 함과 탄력성이 있습니다. 함께, 그들은 당신이 던질 수있는 오류뿐만 아니라 가능한 모든 오류를 포착 해야하는 do/ catch진술로 요약합니다.

함수가 어떤 유형의 오류를 발생시킬 수 있는지 선언하지 않으며, 예외 발생 여부 만 선언합니다. 그것은 무한한 종류의 문제입니다 : 미래의 자기 자신을 포함하여 다른 사람들이 사용할 함수를 정의 할 때, 함수의 모든 클라이언트가 구현의 모든 변경에 적응하도록 할 필요는 없습니다. 던질 수있는 오류를 포함하여 함수를 호출하는 코드가 그러한 변경에 대해 탄력적이기를 원합니다.

함수는 어떤 종류의 오류가 발생하는지 (또는 나중에 발생할 수 있음) 말할 수 없으므로 오류 catch를 잡는 블록은 어떤 종류의 오류가 발생하는지 알 수 없습니다. 따라서 알고있는 오류 유형을 처리하는 것 외에도 범용 catch문 으로 처리하지 않는 오류 유형을 처리해야합니다. 이렇게하면 함수가 향후에 발생하는 오류 세트를 변경하더라도 호출자는 여전히 오류.

do {
    let sandwich = try makeMeSandwich(kitchen)
    print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
    print("Not me error")
} catch SandwichError.DoItYourself {
    print("do it error")
} catch let error {
    print(error.localizedDescription)
}

그러나 거기서 멈추지 마십시오. 이 탄력성 아이디어에 대해 더 생각해보십시오. 샌드위치를 ​​디자인 한 방식에 따라 샌드위치를 ​​사용하는 모든 장소의 오류를 설명해야합니다. 즉, 오류 사례 집합을 변경할 때마다 오류 사례를 사용하는 모든 장소를 변경해야합니다.

자신의 오류 유형을 정의하는 아이디어는 그러한 것을 중앙 집중화하는 것입니다. description오류에 대한 방법을 정의 할 수 있습니다.

extension SandwichError: CustomStringConvertible {
    var description: String {
        switch self {
            case NotMe: return "Not me error"
            case DoItYourself: return "Try sudo"
        }
    }
}

그런 다음 오류 처리 코드는 오류 유형에 자신을 설명하도록 요청할 수 있습니다. 이제 오류를 처리하는 모든 위치에서 동일한 코드를 사용할 수 있으며 향후 가능한 오류 사례도 처리 할 수 ​​있습니다.

do {
    let sandwich = try makeMeSandwich(kitchen)
    print("i eat it \(sandwich)")
} catch let error as SandwichError {
    print(error.description)
} catch {
    print("i dunno")
}

또한 오류 유형 (또는 그 확장명)이 오류를보고하는 다른 방법을 지원할 수있는 방법을 제공합니다. 예를 들어 오류 유형 UIAlertController에 iOS 사용자에게 오류를보고하는 방법을 알려주는 확장명을 가질 수 있습니다 .


1
@rickster : 실제로 컴파일러 오류를 재현 할 수 있습니까? 원래 코드는 오류나 경고없이 컴파일됩니다. 캐치되지 않은 예외가 발생하면 프로그램이으로 중단됩니다. error caught in main()따라서 귀하가 말한 모든 것이 현명하지만 그 동작을 재현 할 수 없습니다.
Martin R

5
확장 프로그램에서 오류 메시지를 분리 한 방법을 좋아하십시오. 코드를 깨끗하게 유지하는 정말 좋은 방법! 좋은 예입니다!
Konrad77

try런타임 코드를 사용하면 런타임 오류가 발생하고 응용 프로그램이 중단 될 수 있으므로 프로덕션 코드에서 강제 표현식을 사용하지 않는 것이 좋습니다.
Otar

@Otar는 일반적으로 좋은 생각이지만 주제와는 조금 다릅니다. 대답은 using (또는 사용하지 않음)을 다루지 않습니다 try!. 또한 생산 코드에 대해서도 스위프트의 다양한 "강제"작업 (포장 풀기, 시도 등)에 대해 유효한 "안전한"사용 사례가있을 수 있습니다. 테스트 할 수없는 오류 처리 코드를 작성하는 것보다 즉각적인 오류로 단락하는 것이 더 합리적입니다.
rickster

오류 메시지를 표시하기 만하면 해당 논리를 SandwichError클래스 안에 넣는 것이 좋습니다. 그러나 대부분의 오류는 오류 처리 논리를 캡슐화 할 수 없다고 생각합니다. 이는 일반적으로 호출자의 컨텍스트에 대한 지식 (복구, 재시도 또는 실패 업스트림보고 여부 등)이 필요하기 때문입니다. 즉, 가장 일반적인 패턴은 특정 오류 유형과 일치해야한다고 생각합니다.
최대

29

나는 이것이 아직 제대로 구현되지 않았다고 생각합니다. 스위프트 프로그래밍 가이드는 확실히 컴파일러는 'switch 문처럼'완전한 일치를 추론 할 수 있음을 의미하는 것으로 보인다. catch철저하기 위해 장군이 필요하다는 언급은 없습니다 .

또한 오류는 try블록의 끝이 아니라 라인 에 있다는 것을 알 수 있습니다 . 즉, 어떤 시점에서 컴파일러는 try블록의 어떤 명령문이 처리되지 않은 예외 유형 을 가졌는지 정확히 알 수 있습니다.

문서는 조금 모호합니다. 'Swift의 새로운 기능'비디오를 살펴본 결과 단서를 찾을 수 없었습니다. 계속 노력하겠습니다.

최신 정보:

우리는 이제 ErrorType 유추에 대한 힌트없이 베타 3까지 올라 왔습니다. 나는 이것이 이것이 계획되어 있다면 (그리고 여전히 어느 시점에 있다고 생각한다.) 프로토콜 확장에 대한 동적 디스패치가 아마도 그것을 망 쳤을 것이다.

베타 4 업데이트 :

Xcode 7b4는에 대한 문서 주석 지원을 추가 Throws:했는데, 이는 "어떤 오류가 발생할 수 있으며 그 이유를 문서화하는 데 사용해야합니다". 적어도 이것이 API 소비자에게 오류를 전달 하는 메커니즘을 제공 한다고 생각합니다 . 문서가있을 때 유형 시스템이 필요한 사람!

또 다른 업데이트 :

자동 ErrorType추론을 기대하면서 시간을 소비하고 그 모델의 한계 가 무엇인지 알아 본 후 마음을 바꿨습니다 . Apple이 대신 구현하기를 바랍니다. 본질적으로 :

// allow us to do this:
func myFunction() throws -> Int

// or this:
func myFunction() throws CustomError -> Int

// but not this:
func myFunction() throws CustomErrorOne, CustomErrorTwo -> Int

또 다른 업데이트

Apple의 오류 처리 이론적 근거는 이제 여기에서 확인할 수 있습니다 . swift-evolution 메일 링리스트 에 대한 흥미로운 토론도있었습니다 . 본질적으로 John McCall은 대부분의 라이브러리가 일반적인 오류 사례를 포함하여 끝날 것이라고 믿고 유형 오류는 상용구와는 별도로 코드에 많은 것을 추가하지 않을 것입니다 ( '포부 블러 프'라는 용어를 사용했습니다). Chris Lattner는 Swift 3에서 복원력 모델을 사용할 수 있다면 타이핑 된 오류에 대해 개방적이라고 말했습니다.


링크 주셔서 감사합니다. John에 의해 설득되지 않음 : "많은 라이브러리에 '기타 오류'유형이 포함된다"고해서 모든 사람에게 "기타 오류"유형이 필요한 것은 아닙니다.
Franklin Yu

명백한 반론은 함수가 어떤 종류의 오류를 발생 시킬지 알 수있는 간단한 방법이 없다는 것입니다. 꽤 성가신 것은 오히려 성가시다.
William T Froggard

4

Swift는 귀하의 사례 진술서가 모든 사례를 다루지 않는다고 걱정하여 기본 사례를 작성해야합니다.

do {
    let sandwich = try makeMeSandwich(kitchen)
    print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
    print("Not me error")
} catch SandwichError.DoItYourself {
    print("do it error")
} catch Default {
    print("Another Error")
}

2
그러나 어색하지 않습니까? 나는 두 가지 경우 만 있으며 모두 catch진술에 나열되어 있습니다.
mustafa

2
이제 추가하는 개선 요청을위한 좋은 시간이다 func method() throws(YourErrorEnum), 또는 throws(YourEnum.Error1, .Error2, .Error3)당신이 던져 질 수있는 것을 알 수 있도록이
마티아스 Bauch

8
WWDC 세션 중 하나의 Swift 컴파일러 팀은 'Java와 같은'가능한 모든 오류의 목록을 원하지 않았다는 것을 분명히했습니다.
Sam

4
기본 / 기본 오류가 없습니다. 다른 포스터가 지적했듯이 그냥 빈 캐치 {}를 남겨주세요
Opus1217

1
@Icaro 그것은 나를 안전하게 만들지 못한다. 나중에 "배열에 새 항목을 추가"하는 경우 컴파일러는 영향을받는 모든 catch 절을 업데이트하지 않았다고 소리 쳐야합니다.
Franklin Yu

3

함수가 던질 수있는 유형이 없기 때문에 실망했지만, @rickster 덕분에 이제 얻을 수 있습니다. 함께 던지는 유형을 지정할 수 있다고 가정 해 보겠습니다.

enum MyError: ErrorType { case ErrorA, ErrorB }

func myFunctionThatThrows() throws MyError { ...throw .ErrorA...throw .ErrorB... }

do {
    try myFunctionThatThrows()
}
case .ErrorA { ... }
case .ErrorB { ... }

문제는 myFunctionThatThrows에서 아무것도 변경하지 않더라도 MyError에 오류 사례를 추가하면 다음과 같습니다.

enum MyError: ErrorType { case ErrorA, ErrorB, ErrorC }

우리의 do / try / catch가 더 이상 철저하지 않고 MyError를 던지는 함수를 호출 한 다른 장소 때문에 망가졌습니다.


3
왜 당신이 망쳐 졌는지 잘 모르겠습니다. 컴파일러 오류가 발생합니다. 원하는 것이 맞습니까? 열거 형 사례를 추가하면 명령문이 전환됩니다.
Sam

어떤 의미에서는 이것이 오류 열거 형 / do case에서 발생할 가능성이 가장 높았지만 열거 형 / 스위치에서 발생하는 것과 똑같습니다. 나는 우리가 던지는 것을 타이핑하지 않는 Apple의 선택이 좋은 것이라고 스스로를 설득하려고 노력하고 있지만, 당신은 나에게 도움이되지 않습니다! ^^
greg3z 2016 년

던진 오류를 수동으로 입력하면 사소하지 않은 경우 큰 혼란이 생길 ​​수 있습니다. 유형은 함수 내에서 모든 throw 및 try 문에서 발생 가능한 모든 오류의 합집합입니다. 오류 열거 형을 수동으로 유지 관리하는 경우 어려움이 있습니다. catch {}모든 블록의 맨 아래에있는 A 는 틀림없이 더 나쁩니다. 컴파일러가 결국 오류 유형을 자동으로 유추하기를 희망하지만 확인할 수는 없었습니다.
Sam

컴파일러가 이론적으로 함수가 발생하는 오류 유형을 유추 할 수 있어야한다는 데 동의합니다. 그러나 나는 명확하게하기 위해 개발자가 명시 적으로 적어 놓는 것도 의미가 있다고 생각합니다. 사소한 경우에는 다른 오류 유형을 나열해도 괜찮습니다. func f () throw ErrorTypeA, ErrorTypeB {}
greg3z

오류 유형 (문서 주석 제외)을 전달하는 메커니즘이 없다는 점에서 분명히 누락 된 부분이 있습니다. 그러나 Swift 팀은 명시적인 오류 유형 목록을 원하지 않는다고 말했습니다. 과거에 Java에서 확인 된 예외를 처리 한 대부분의 사람들이 동의 할 것이라고 확신합니다.
Sam

1
enum NumberError: Error {
  case NegativeNumber(number: Int)
  case ZeroNumber
  case OddNumber(number: Int)
}

extension NumberError: CustomStringConvertible {
         var description: String {
         switch self {
             case .NegativeNumber(let number):
                 return "Negative number \(number) is Passed."
             case .OddNumber(let number):
                return "Odd number \(number) is Passed."
             case .ZeroNumber:
                return "Zero is Passed."
      }
   }
}

 func validateEvenNumber(_ number: Int) throws ->Int {
     if number == 0 {
        throw NumberError.ZeroNumber
     } else if number < 0 {
        throw NumberError.NegativeNumber(number: number)
     } else if number % 2 == 1 {
         throw NumberError.OddNumber(number: number)
     }
    return number
}

이제 번호 확인 :

 do {
     let number = try validateEvenNumber(0)
     print("Valid Even Number: \(number)")
  } catch let error as NumberError {
     print(error.description)
  }

-2

다음과 같이 열거 형을 만듭니다.

//Error Handling in swift
enum spendingError : Error{
case minus
case limit
}

다음과 같은 방법을 작성하십시오.

 func calculateSpending(morningSpending:Double,eveningSpending:Double) throws ->Double{
if morningSpending < 0 || eveningSpending < 0{
    throw spendingError.minus
}
if (morningSpending + eveningSpending) > 100{
    throw spendingError.limit
}
return morningSpending + eveningSpending
}

이제 오류가 있는지 확인하고 처리하십시오.

do{
try calculateSpending(morningSpending: 60, eveningSpending: 50)
} catch spendingError.minus{
print("This is not possible...")
} catch spendingError.limit{
print("Limit reached...")
}

닫지 만 시가는 없습니다. 간격을 고정하고 열거 형 낙타 케이스를 만들어보십시오.
Alec
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.