나는 Swift를 너무 많이 읽지 않았지만 주목할 점은 예외가 없다는 것입니다. 그렇다면 Swift에서 오류 처리는 어떻게합니까? 누구든지 오류 처리와 관련된 것을 발견 했습니까?
답변
스위프트 2 & 3
새로운 오류 처리 메커니즘이 있기 때문에 예외와 다소 유사하지만 세부 사항이 다르기 때문에 Swift 2에서는 상황이 약간 변경되었습니다.
1. 오류 가능성 표시
함수 / 메소드에 오류가 발생할 수 있음을 나타내려면 다음 throws
과 같은 키워드 를 포함해야합니다.
func summonDefaultDragon() throws -> Dragon
참고 : 함수가 실제로 던질 수있는 오류 유형에 대한 사양은 없습니다. 이 선언은 단순히 함수가 ErrorType을 구현하는 모든 유형의 인스턴스를 던질 수 있거나 전혀 던지지 않음을 나타냅니다.
2. 오류를 발생시킬 수있는 함수 호출
함수를 호출하려면 다음과 같이 try 키워드를 사용해야합니다.
try summonDefaultDragon()
이 줄은 일반적으로 이와 같은 do-catch 블록이 있어야합니다
do {
let dragon = try summonDefaultDragon()
} catch DragonError.dragonIsMissing {
// Some specific-case error-handling
} catch DragonError.notEnoughMana(let manaRequired) {
// Other specific-case error-handlng
} catch {
// Catch all error-handling
}
참고 : catch 절은 Swift 패턴 일치의 모든 강력한 기능을 사용하므로 여기에서 매우 유연합니다.
throws
키워드 자체로 표시된 함수에서 throwing 함수를 호출하는 경우 오류를 전파하기로 결정할 수 있습니다 .
func fulfill(quest: Quest) throws {
let dragon = try summonDefaultDragon()
quest.ride(dragon)
}
또는 다음을 사용하여 투사 기능을 호출 할 수 있습니다 try?
.
let dragonOrNil = try? summonDefaultDragon()
이렇게하면 오류가 발생하면 반환 값을 얻거나 nil을 얻습니다. 이 방법을 사용하면 오류 개체가 나타나지 않습니다.
즉, 다음 try?
과 같은 유용한 문장과 결합 할 수도 있습니다 .
if let dragon = try? summonDefaultDragon()
또는
guard let dragon = try? summonDefaultDragon() else { ... }
마지막으로, 실제로 오류가 발생하지 않는다는 것을 알 수 있으며 (예 : 이미 확인한 전제 조건이므로) try!
키워드를 사용하십시오 .
let dragon = try! summonDefaultDragon()
함수에서 실제로 오류가 발생하면 응용 프로그램에 런타임 오류가 발생하고 응용 프로그램이 종료됩니다.
3. 오류 발생
오류를 발생시키기 위해 다음과 같이 throw 키워드를 사용하십시오
throw DragonError.dragonIsMissing
ErrorType
프로토콜에 맞는 것을 던질 수 있습니다 . 초보자 NSError
는이 프로토콜을 준수하지만 열거 형 ErrorType
을 사용하여 여러 관련 오류를 잠재적으로 다음과 같은 추가 데이터 조각으로 그룹화 할 수 있습니다.
enum DragonError: ErrorType {
case dragonIsMissing
case notEnoughMana(requiredMana: Int)
...
}
새로운 Swift 2 & 3 오류 메커니즘과 Java / C # / C ++ 스타일 예외의 주요 차이점은 다음과 같습니다.
- 구문은 약간 다릅니다 :
do-catch
+try
+defer
와 기존try-catch-finally
구문. - 예외 처리는 일반적으로 성공 경로보다 예외 경로에서 훨씬 더 높은 실행 시간을 발생시킵니다. 성공 경로와 오류 경로의 비용이 거의 같은 Swift 2.0 오류의 경우에는 그렇지 않습니다.
- 모든 오류 발생 코드를 선언해야하지만 예외는 어디에서나 발생했을 수 있습니다. Java 명명법에서는 모든 오류가 “체크 된 예외”입니다. 그러나 Java와 달리 잠재적으로 발생 된 오류를 지정하지 않습니다.
- 스위프트 예외는 ObjC 예외와 호환되지 않습니다. 당신의
do-catch
당신이 ObjC를 사용해야합니다 그것을 위해 블록은, 반대로 어떤 NSException, 그리고 그 반대를 잡을 수 없습니다. - Swift 예외는 ( 함수 를 리턴 하기 위해 ) 또는 ( 함수 를 리턴 하기 위해) 리턴 하고 오류 세부 사항을 전달하는 Cocoa
NSError
메소드 규칙 과 호환됩니다 .false
Bool
nil
AnyObject
NSErrorPointer
오류 처리를 용이하게하는 여분의 합성 설탕으로서 두 가지 개념이 더 있습니다
defer
Java / C # / etc의 finally 블록과 동일한 효과를 얻을 수있는 지연된 동작 ( 키워드 사용 )- 가드 문 (
guard
키워드 사용 )을 사용 하면 일반적인 오류 확인 / 신호 코드보다 if / else 코드를 적게 작성할 수 있습니다.
스위프트 1
런타임 오류 :
Leandros는 네트워크 연결 문제, 데이터 구문 분석, 파일 열기 등과 같은 런타임 오류 처리에 NSError
대해 Foundation, AppKit, UIKit 등이 이러한 방식으로 오류를보고하므로 ObjC에서와 같이 사용해야합니다 . 언어보다 프레임 워크가 더 중요합니다.
AFNetworking과 같은 분리기 성공 / 실패 블록이 사용되는 또 다른 빈번한 패턴은 다음과 같습니다.
var sessionManager = AFHTTPSessionManager(baseURL: NSURL(string: "yavin4.yavin.planets"))
sessionManager.HEAD("/api/destoryDeathStar", parameters: xwingSquad,
success: { (NSURLSessionDataTask) -> Void in
println("Success")
},
failure:{ (NSURLSessionDataTask, NSError) -> Void in
println("Failure")
})
여전히 오류 블록 NSError
은 오류를 설명하는 인스턴스를 자주 받았습니다 .
프로그래머 오류 :
프로그래머 오류 (배열 요소의 범위를 벗어난 액세스, 함수 호출에 전달 된 잘못된 인수 등)의 경우 ObjC에서 예외를 사용했습니다. Swift 언어는 예외 (예 throw
: catch
, 등의 키워드)에 대한 언어 지원이없는 것 같습니다 . 그러나 문서에 따르면 ObjC와 동일한 런타임에서 실행 중이므로 여전히 NSExceptions
다음과 같이 던질 수 있습니다 .
NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()
ObjC 코드에서 예외를 포착하도록 선택할 수도 있지만 순수한 Swift에서는이를 포착 할 수 없습니다.
문제는 프로그래머 오류에 대한 예외를 던지거나 언어 가이드에서 Apple이 제안한 어설 션을 사용해야하는지 여부입니다.
답변
2015 년 6 월 9 일 업데이트-매우 중요
스위프트 2.0 함께 제공 try
, throw
및 catch
키워드와 가장 흥미로운입니다 :
Swift는 오류를 생성하는 Objective-C 메소드를 Swift의 기본 오류 처리 기능에 따라 오류를 발생시키는 메소드로 자동 변환합니다.
참고 : NSError 객체 인수와 함께 완료 처리기를 사용하는 대리자 메서드 또는 메서드와 같이 오류를 소비하는 메서드는 Swift에서 가져올 때 throw되는 메서드가 아닙니다.
발췌 : Apple Inc.“Cocoa 및 Objective-C와 함께 Swift 사용 (Swift 2 시험판).” iBooks.
예 : (책에서)
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *URL = [NSURL fileURLWithPath:@"/path/to/file"];
NSError *error = nil;
BOOL success = [fileManager removeItemAtURL:URL error:&error];
if (!success && error){
NSLog(@"Error: %@", error.domain);
}
신속한 대응은 다음과 같습니다.
let fileManager = NSFileManager.defaultManager()
let URL = NSURL.fileURLWithPath("path/to/file")
do {
try fileManager.removeItemAtURL(URL)
} catch let error as NSError {
print ("Error: \(error.domain)")
}
오류 발생 :
*errorPtr = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotOpenFile userInfo: nil]
발신자에게 자동으로 전파됩니다.
throw NSError(domain: NSURLErrorDomain, code: NSURLErrorCannotOpenFile, userInfo: nil)
Apple의 책에서 Swift Programming Language는 enum을 사용하여 오류를 처리해야합니다.
다음은이 책의 예입니다.
enum ServerResponse {
case Result(String, String)
case Error(String)
}
let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")
switch success {
case let .Result(sunrise, sunset):
let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
let serverResponse = "Failure... \(error)"
}
출처 : Apple Inc. “Swift Programming Language” iBooks. https://itun.es/br/jEUH0.l
최신 정보
Apple 뉴스 서적, “Cocoa 및 Objective-C와 함께 Swift 사용”. 신속한 언어를 사용하면 런타임 예외가 발생하지 않으므로 try-catch가 없습니다. 대신 옵션 체인 을 사용 합니다.
이 책의 내용은 다음과 같습니다.
예를 들어 아래 코드 목록에서 length 속성과 characterAtIndex : 메서드가 NSDate 개체에 없기 때문에 첫 번째와 두 번째 줄은 실행되지 않습니다. myLength 상수는 선택적 Int 인 것으로 추론되며 nil로 설정됩니다. if-let 문을 사용하여 3 행에 표시된 것처럼 오브젝트가 응답하지 않을 수있는 메소드의 결과를 조건부 랩 해제 할 수도 있습니다.
let myLength = myObject.length?
let myChar = myObject.characterAtIndex?(5)
if let fifthCharacter = myObject.characterAtIndex(5) {
println("Found \(fifthCharacter) at index 5")
}
발췌 : Apple Inc.“Cocoa 및 Objective-C와 함께 Swift 사용” iBooks. https://itun.es/br/1u3-0.l
또한이 책은 Objective-C (NSError Object)의 코코아 오류 패턴을 사용하도록 권장합니다.
Swift의 오류보고는 Objective-C에서와 동일한 패턴을 따르며 선택적 반환 값을 제공 할 수 있다는 이점이 있습니다. 가장 간단한 경우 함수에서 Bool 값을 반환하여 성공했는지 여부를 나타냅니다. 오류 이유를보고해야하는 경우 NSErrorPointer 유형의 NSError 출력 매개 변수를 함수에 추가 할 수 있습니다. 이 유형은 추가 메모리 안전 및 선택적 입력을 통해 Objective-C의 NSError **와 거의 동일합니다. 아래 코드 목록에 표시된 것처럼 접두사 및 연산자를 사용하여 선택적 NSError 유형에 대한 참조를 NSErrorPointer 객체로 전달할 수 있습니다.
var writeError : NSError?
let written = myString.writeToFile(path, atomically: false,
encoding: NSUTF8StringEncoding,
error: &writeError)
if !written {
if let error = writeError {
println("write failure: \(error.localizedDescription)")
}
}
발췌 : Apple Inc.“Cocoa 및 Objective-C와 함께 Swift 사용” iBooks. https://itun.es/br/1u3-0.l
답변
Objective-C의 접근 방식과 비슷한 Swift에는 예외가 없습니다.
개발 과정에서 assert
나타날 수있는 오류를 파악하고 프로덕션 환경으로 가기 전에 수정해야 할 때 사용할 수 있습니다.
고전적인 NSError
접근 방식은 변경되지 않고을 보내면 NSErrorPointer
채워집니다.
간단한 예 :
var error: NSError?
var contents = NSFileManager.defaultManager().contentsOfDirectoryAtPath("/Users/leandros", error: &error)
if let error = error {
println("An error occurred \(error)")
} else {
println("Contents: \(contents)")
}
답변
권장되는 ‘Swift Way’는 다음과 같습니다.
func write(path: String)(#error: NSErrorPointer) -> Bool { // Useful to curry error parameter for retrying (see below)!
return "Hello!".writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: error)
}
var writeError: NSError?
let written = write("~/Error1")(error: &writeError)
if !written {
println("write failure 1: \(writeError!.localizedDescription)")
// assert(false) // Terminate program
}
그러나 오류 처리를 끝에 별도의 블록으로 이동하기 때문에 추적하기가 더 쉬우므로 try / catch를 선호합니다.이 배열을 “황금 경로”라고도합니다. 운이 좋으면 클로저 로이 작업을 수행 할 수 있습니다.
TryBool {
write("~/Error2")(error: $0) // The code to try
}.catch {
println("write failure 2: \($0!.localizedDescription)") // Report failure
// assert(false) // Terminate program
}
또한 재시도 기능을 쉽게 추가 할 수 있습니다.
TryBool {
write("~/Error3")(error: $0) // The code to try
}.retry {
println("write failure 3 on try \($1 + 1): \($0!.localizedDescription)")
return write("~/Error3r") // The code to retry
}.catch {
println("write failure 3 catch: \($0!.localizedDescription)") // Report failure
// assert(false) // Terminate program
}
TryBool의 리스팅은 다음과 같습니다 :
class TryBool {
typealias Tryee = NSErrorPointer -> Bool
typealias Catchee = NSError? -> ()
typealias Retryee = (NSError?, UInt) -> Tryee
private var tryee: Tryee
private var retries: UInt = 0
private var retryee: Retryee?
init(tryee: Tryee) {
self.tryee = tryee
}
func retry(retries: UInt, retryee: Retryee) -> Self {
self.retries = retries
self.retryee = retryee
return self
}
func retry(retryee: Retryee) -> Self {
return self.retry(1, retryee)
}
func retry(retries: UInt) -> Self {
// For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
self.retries = retries
retryee = nil
return self
}
func retry() -> Self {
return retry(1)
}
func catch(catchee: Catchee) {
var error: NSError?
for numRetries in 0...retries { // First try is retry 0
error = nil
let result = tryee(&error)
if result {
return
} else if numRetries != retries {
if let r = retryee {
tryee = r(error, numRetries)
}
}
}
catchee(error)
}
}
Bool 값 대신 Optional 반환 값을 테스트하기 위해 비슷한 클래스를 작성할 수 있습니다.
class TryOptional<T> {
typealias Tryee = NSErrorPointer -> T?
typealias Catchee = NSError? -> T
typealias Retryee = (NSError?, UInt) -> Tryee
private var tryee: Tryee
private var retries: UInt = 0
private var retryee: Retryee?
init(tryee: Tryee) {
self.tryee = tryee
}
func retry(retries: UInt, retryee: Retryee) -> Self {
self.retries = retries
self.retryee = retryee
return self
}
func retry(retryee: Retryee) -> Self {
return retry(1, retryee)
}
func retry(retries: UInt) -> Self {
// For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
self.retries = retries
retryee = nil
return self
}
func retry() -> Self {
return retry(1)
}
func catch(catchee: Catchee) -> T {
var error: NSError?
for numRetries in 0...retries {
error = nil
let result = tryee(&error)
if let r = result {
return r
} else if numRetries != retries {
if let r = retryee {
tryee = r(error, numRetries)
}
}
}
return catchee(error)
}
}
TryOptional 버전은 선택적 프로그래밍이 아닌 리턴 유형을 적용하여 ‘Swift Way :
struct FailableInitializer {
init?(_ id: Int, error: NSErrorPointer) {
// Always fails in example
if error != nil {
error.memory = NSError(domain: "", code: id, userInfo: [:])
}
return nil
}
private init() {
// Empty in example
}
static let fallback = FailableInitializer()
}
func failableInitializer(id: Int)(#error: NSErrorPointer) -> FailableInitializer? { // Curry for retry
return FailableInitializer(id, error: error)
}
var failError: NSError?
var failure1Temp = failableInitializer(1)(error: &failError)
if failure1Temp == nil {
println("failableInitializer failure code: \(failError!.code)")
failure1Temp = FailableInitializer.fallback
}
let failure1 = failure1Temp! // Unwrap
TryOptional 사용하기 :
let failure2 = TryOptional {
failableInitializer(2)(error: $0)
}.catch {
println("failableInitializer failure code: \($0!.code)")
return FailableInitializer.fallback
}
let failure3 = TryOptional {
failableInitializer(3)(error: $0)
}.retry {
println("failableInitializer failure, on try \($1 + 1), code: \($0!.code)")
return failableInitializer(31)
}.catch {
println("failableInitializer failure code: \($0!.code)")
return FailableInitializer.fallback
}
자동 언 랩핑을 참고하십시오.
답변
편집 : 이 답변은 효과가 있지만 Objective-C가 Swift로 음역되는 것 이상입니다. Swift 2.0의 변경으로 인해 더 이상 사용되지 않습니다. 위의 Guilherme Torres Castro의 답변은 Swift에서 오류를 처리하는 기본 방법에 대한 아주 좋은 소개입니다. VOS
알아내는 데 약간의 시간이 걸렸지 만 나는 그것을 생각한 것 같습니다. 그래도 추한 것 같습니다. Objective-C 버전의 얇은 스킨에 지나지 않습니다.
NSError 매개 변수를 사용하여 함수 호출 …
var fooError : NSError ? = nil
let someObject = foo(aParam, error:&fooError)
// Check something was returned and look for an error if it wasn't.
if !someObject {
if let error = fooError {
// Handle error
NSLog("This happened: \(error.localizedDescription)")
}
} else {
// Handle success
}`
에러 파라미터를받는 함수 작성 중 …
func foo(param:ParamObject, error: NSErrorPointer) -> SomeObject {
// Do stuff...
if somethingBadHasHappened {
if error {
error.memory = NSError(domain: domain, code: code, userInfo: [:])
}
return nil
}
// Do more stuff...
}
답변
시험 Catch 기능을 제공하는 objective C 주변의 기본 래퍼입니다.
https://github.com/williamFalcon/SwiftTryCatch
다음과 같이 사용하십시오.
SwiftTryCatch.try({ () -> Void in
//try something
}, catch: { (error) -> Void in
//handle error
}, finally: { () -> Void in
//close resources
})
답변
이것은 swift 2.0에 대한 업데이트 답변입니다. Java와 같은 기능이 풍부한 오류 처리 모델을 기대하고 있습니다. 마침내 그들은 좋은 소식을 발표했습니다. 여기
오류 처리 모델 : Swift 2.0의 새로운 오류 처리 모델은 익숙한 try, throw 및 catch 키워드를 사용 하여 자연스럽게 느껴집니다 . 무엇보다도 Apple SDK 및 NSError와 완벽하게 작동하도록 설계되었습니다. 실제로 NSError는 Swift의 ErrorType을 따릅니다. Swift의 새로운 기능에 대한 WWDC 세션을 확실히보고 싶을 것입니다.
예 :
func loadData() throws { }
func test() {
do {
try loadData()
} catch {
print(error)
}}