나는 Swift를 너무 많이 읽지 않았지만 주목할 점은 예외가 없다는 것입니다. 그렇다면 Swift에서 오류 처리는 어떻게합니까? 누구든지 오류 처리와 관련된 것을 발견 했습니까?
나는 Swift를 너무 많이 읽지 않았지만 주목할 점은 예외가 없다는 것입니다. 그렇다면 Swift에서 오류 처리는 어떻게합니까? 누구든지 오류 처리와 관련된 것을 발견 했습니까?
답변:
새로운 오류 처리 메커니즘이 있기 때문에 예외와 다소 유사하지만 세부 사항이 다르기 때문에 Swift 2에서는 상황이 약간 변경되었습니다.
함수 / 메소드에 오류가 발생할 수 있음을 나타내려면 다음 throws
과 같은 키워드 를 포함해야합니다.
func summonDefaultDragon() throws -> Dragon
참고 : 함수가 실제로 던질 수있는 오류 유형에 대한 사양은 없습니다. 이 선언은 단순히 함수가 ErrorType을 구현하는 모든 유형의 인스턴스를 던질 수 있거나 전혀 던지지 않음을 나타냅니다.
함수를 호출하려면 다음과 같이 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()
함수에서 실제로 오류가 발생하면 응용 프로그램에 런타임 오류가 발생하고 응용 프로그램이 종료됩니다.
오류를 발생시키기 위해 다음과 같이 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
구문.do-catch
당신이 ObjC를 사용해야합니다 그것을 위해 블록은, 반대로 어떤 NSException, 그리고 그 반대를 잡을 수 없습니다.NSError
메소드 규칙 과 호환됩니다 .false
Bool
nil
AnyObject
NSErrorPointer
오류 처리를 용이하게하는 여분의 합성 설탕으로서 두 가지 개념이 더 있습니다
defer
Java / C # / etc의 finally 블록과 동일한 효과를 얻을 수있는 지연된 동작 ( 키워드 사용 )guard
키워드 사용 )을 사용 하면 일반적인 오류 확인 / 신호 코드보다 if / else 코드를 적게 작성할 수 있습니다.런타임 오류 :
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이 제안한 어설 션을 사용해야하는지 여부입니다.
fatalError(...)
아니라 동일합니다.
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)")
}
f();g();
것이 f(&err);if(err) return;g(&err);if(err) return;
첫 달이되게되었고, 이제 막f(nil);g(nil);hopeToGetHereAlive();
권장되는 '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)
}}
Guilherme 토레스 카스트로가 말했듯이, 스위프트 2.0, try
, catch
, do
프로그래밍에 사용할 수 있습니다.
예를 들어,에서 CoreData 대신 넣어의 데이터 방법을 가져 &error
에 매개 변수로 managedContext.executeFetchRequest(fetchRequest, error: &error)
지금 우리는 사용을 사용할 필요가, managedContext.executeFetchRequest(fetchRequest)
다음으로 오류를 처리 try
, catch
( 애플의 문서 링크 )
do {
let fetchedResults = try managedContext.executeFetchRequest(fetchRequest) as? [NSManagedObject]
if let results = fetchedResults{
people = results
}
} catch {
print("Could not fetch")
}
xcode7 베타를 이미 다운로드 한 경우 설명서 및 API 참조 에서 오류 발생 을 검색 하고 첫 번째로 표시되는 결과를 선택하면이 새로운 구문에 대해 수행 할 수있는 기본 아이디어가 제공됩니다. 그러나 많은 API에 대한 완전한 문서는 아직 게시되지 않았습니다.
더 멋진 오류 처리 기술은
Swift의 새로운 기능 (2015 세션 106 28m30s)
오류 처리는 Swift 2.0의 새로운 기능입니다. 그것은 사용 try
, throw
및 catch
키워드를.
공식 Apple Swift 블로그 에서 Apple Swift 2.0 발표를 참조하십시오
예외를 처리하기위한 훌륭하고 간단한 라이브러리 : TryCatchFinally-Swift
다른 몇 가지와 마찬가지로 객관적인 C 예외 기능을 둘러 쌉니다.
다음과 같이 사용하십시오.
try {
println(" try")
}.catch { e in
println(" catch")
}.finally {
println(" finally")
}
다른 사람들이 이미 언급했듯이 Swift 2부터는 오류 처리는 do / try / catch 및 ErrorType 열거 형을 사용하여 가장 잘 수행됩니다. 이 방법은 동기식 방법에는 적합하지만 비동기식 오류 처리에는 약간의 영리함이 필요합니다.
이 기사에는이 문제에 대한 훌륭한 접근 방식이 있습니다.
https://jeremywsherman.com/blog/2015/06/17/using-swift-throws-with-completion-callbacks/
요약:
// create a typealias used in completion blocks, for cleaner code
typealias LoadDataResult = () throws -> NSData
// notice the reference to the typealias in the completionHandler
func loadData(someID: String, completionHandler: LoadDataResult -> Void)
{
completionHandler()
}
위의 메소드에 대한 호출은 다음과 같습니다.
self.loadData("someString",
completionHandler:
{ result: LoadDataResult in
do
{
let data = try result()
// success - go ahead and work with the data
}
catch
{
// failure - look at the error code and handle accordingly
}
})
이것은 별도의 errorHandler 콜백을 비동기 함수에 전달하는 것보다 조금 더 깨끗해 보입니다. 이는 Swift 2 이전에 처리 된 방식이었습니다.
내가 본 것은 장치의 특성으로 인해 사용자에게 많은 암호 오류 처리 메시지를 던지고 싶지 않다는 것입니다. 그렇기 때문에 대부분의 함수는 선택적 값을 반환하고 옵션을 무시하도록 코드를 작성합니다. 함수가 nil을 반환하면 실패했다는 메시지 또는 기타 메시지를 표시 할 수 있습니다.