Obsługa błędów w języku Swift


190

Nie czytałem zbyt wiele w Swift, ale zauważyłem, że nie ma wyjątków. Jak więc radzą sobie z obsługą błędów w Swift? Czy ktoś znalazł coś związanego z obsługą błędów?


1
Znalazłem komunikaty o błędach, podobnie jak w przypadku Obj-C: o
Arbitur

13
@Arbitur stary dobry segfault sposób?
peko

Utworzyłem NSTimer w Swift, a kiedy źle napisałem funkcję, zawiesił się i dał mi błąd, mówiąc, że nie może znaleźć metody :)
Arbitur

3
Możesz dodać obsługę try-catch dla Swift, postępując zgodnie z instrukcjami w tym artykule: medium.com/@_willfalcon/adding-try-catch-to-swift-71ab27bcb5b8
William Falcon

@peko Jak radzisz sobie z segfault w Swift? Nie sądzę, że jest to możliwe na teraz, co niestety sprawia, że ​​niektóre błędy są niemożliwe do odzyskania
Orlin Georgiev

Odpowiedzi:


148

Swift 2 i 3

W Swift 2 wszystko się nieco zmieniło, ponieważ wprowadzono nowy mechanizm obsługi błędów, który jest nieco bardziej podobny do wyjątków, ale różni się szczegółowo.

1. Wskazanie możliwości wystąpienia błędu

Jeśli funkcja / metoda chce wskazać, że może zgłosić błąd, powinna zawierać throwstakie słowo kluczowe

func summonDefaultDragon() throws -> Dragon

Uwaga: nie ma specyfikacji rodzaju błędu, który funkcja może faktycznie wyrzucić. Ta deklaracja po prostu stwierdza, że ​​funkcja może wyrzucić instancję dowolnego typu implementującą ErrorType lub w ogóle nie rzuca.

2. Wywoływanie funkcji, która może zgłaszać błędy

Aby wywołać funkcję, musisz użyć słowa kluczowego try, takiego jak ten

try summonDefaultDragon()

ta linia powinna normalnie występować w bloku catch-catch w ten sposób

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
}

Uwaga: klauzula catch wykorzystuje wszystkie zaawansowane funkcje dopasowania wzoru Swift, dzięki czemu jesteś bardzo elastyczny.

Możesz zdecydować się na propagowanie błędu, jeśli wywołujesz funkcję rzucania z funkcji, która sama jest oznaczona throwssłowem kluczowym:

func fulfill(quest: Quest) throws {
    let dragon = try summonDefaultDragon()
    quest.ride(dragon)
} 

Alternatywnie możesz wywołać funkcję rzucania za pomocą try?:

let dragonOrNil = try? summonDefaultDragon()

W ten sposób otrzymasz albo wartość zwracaną, albo zero, jeśli wystąpi jakikolwiek błąd. W ten sposób nie otrzymujesz obiektu błędu.

Co oznacza, że ​​możesz także łączyć się try?z przydatnymi stwierdzeniami, takimi jak:

if let dragon = try? summonDefaultDragon()

lub

guard let dragon = try? summonDefaultDragon() else { ... }

Na koniec możesz zdecydować, że wiesz, że błąd faktycznie nie wystąpi (np. Ponieważ już sprawdziłeś, czy są wymagane) i użyć try!słowa kluczowego:

let dragon = try! summonDefaultDragon()

Jeśli funkcja faktycznie zgłasza błąd, wówczas w aplikacji pojawi się błąd czasu wykonywania, a aplikacja zostanie zakończona.

3. Zgłaszanie błędu

Aby zgłosić błąd, użyj słowa kluczowego rzutu takiego jak ten

throw DragonError.dragonIsMissing

Możesz rzucać wszystko, co jest zgodne z ErrorTypeprotokołem. Na początek NSErrorjest zgodny z tym protokołem, ale prawdopodobnie chciałbyś skorzystać z metody wyliczania, ErrorTypektóra umożliwia grupowanie wielu powiązanych błędów, potencjalnie z dodatkowymi częściami danych, takimi jak ten

enum DragonError: ErrorType {
    case dragonIsMissing
    case notEnoughMana(requiredMana: Int)
    ...
}

Główne różnice między nowym mechanizmem błędów Swift 2 i 3 a wyjątkami w stylu Java / C # / C ++ są następujące:

  • Składnia jest nieco inna: do-catch+ try+ defervs. tradycyjnytry-catch-finally składnią .
  • Obsługa wyjątków zwykle wiąże się ze znacznie dłuższym czasem wykonania w ścieżce wyjątku niż w ścieżce sukcesu. Nie dzieje się tak w przypadku błędów Swift 2.0, w których ścieżka sukcesu i ścieżka błędu kosztują mniej więcej tyle samo.
  • Cały kod zgłaszający błąd musi zostać zadeklarowany, a wyjątki mogły zostać zgłoszone z dowolnego miejsca. Wszystkie błędy są „sprawdzonymi wyjątkami” w nomenklaturze Java. Jednak w przeciwieństwie do Javy nie określa się potencjalnie zgłaszanych błędów.
  • Szybkie wyjątki nie są kompatybilne z wyjątkami ObjC. Twójdo-catch blok nie złapie żadnego wyjątku NSEx i vice versa, do tego musisz użyć ObjC.
  • Szybkie wyjątki są zgodne z NSErrorkonwencjami metod Cocoa dotyczącymi zwracania albo false(dla Boolzwracanych funkcji) albo nil(dla AnyObjectzwracanych funkcji) i przekazywania NSErrorPointerze szczegółami błędu.

Jako dodatkowy cukier syntetyczny ułatwiający obsługę błędów, istnieją jeszcze dwie koncepcje

  • odroczone działania (przy użyciu defer słowa kluczowego), które pozwalają osiągnąć ten sam efekt, co ostatecznie bloki w Javie / C # / itp
  • instrukcja guard (za pomocą guardsłowa kluczowego), która pozwala napisać niewiele mniej kodu if / else niż w normalnym kodzie sprawdzania / sygnalizacji błędów

Szybki 1

Błędy czasu wykonania:

Jak sugeruje Leandros do obsługi błędów środowiska wykonawczego (takich jak problemy z połączeniem sieciowym, analizowanie danych, otwieranie pliku itp.), Powinieneś używać NSErrortak jak w ObjC, ponieważ Foundation, AppKit, UIKit itp. Zgłaszają swoje błędy w ten sposób. Więc jest to bardziej kwestia frameworka niż języka.

Innym częstym wzorcem, który jest używany, są bloki sukcesu / awarii separatora, takie jak w 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")
    })

Nadal często otrzymywana jest blokada awarii NSErroropisująca błąd.

Błędy programatora:

W przypadku błędów programisty (takich jak przekroczenie granic elementu tablicy, niepoprawne argumenty przekazane do wywołania funkcji itp.) Zastosowano wyjątki w ObjC. Język Swift nie wydaje się mieć żadnego wsparcia językowego dla wyjątkami (jak throw, catchitp słów kluczowych). Jednak, jak sugeruje dokumentacja, działa on w tym samym środowisku uruchomieniowym co ObjC, dlatego nadal możesz rzucać w NSExceptionsten sposób:

NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()

Po prostu nie możesz ich złapać w czystym Swift, chociaż możesz zdecydować się na łapanie wyjątków w kodzie ObjC.

Pytanie brzmi, czy należy zgłaszać wyjątki dla błędów programisty, czy raczej stosować twierdzenia, jak sugeruje Apple w przewodniku językowym.


20
„problemy z łącznością sieciową” i „otwieranie plików” za pomocą interfejsów API Cocoa (NSFileHandle) mogą generować wyjątki, które należy wychwycić. Bez wyjątków w Swift, musisz zaimplementować tę część programu w Objective-C lub wykonać całą pracę przy użyciu interfejsów API BSD C (oba są słabymi obejściami). Zobacz dokumentację NSFileHandle.writeData, aby uzyskać więcej ... developer.apple.com/library/ios/documentation/Cocoa/Reference/… :
Matt Gallagher

5
Ponownie, brak obsługi wyjątków oznacza dwuetapową konstrukcję obiektu ze wszystkimi nieodłącznymi problemami. Zobacz stroustrup.com/except.pdf .
Phil

2
fatalError(...)jest taka sama, jak również.
holex

8
Tak jak lubię Swifta, myślę, że jest to katastrofalny wybór, a po posmakowaniu niektórych konsekwencji bawią się ogniem z tym pominięciem ...
Rob

2
Tak, sprawdzone wyjątki są teraz używane oszczędnie, ponieważ stwierdzono, że zmuszając programistę do wychwytywania wyjątków, nie mają one nadziei na odzyskanie kodu zanieczyszczającego w sposób łamający zasadę pojedynczej odpowiedzialności. Klasa na poziomie domeny nie chce zajmować się wyjątkami warstwy infrastruktury. Tak więc teraz niesprawdzone wyjątki są zwykle preferowane, a zainteresowana strona może je złapać, jeśli to właściwe. . Sprawdzone = zdecydowanie możliwe do odzyskania. Niezaznaczone = niemożliwe / potencjalnie możliwe do odzyskania.
Jasper Blues

69

Aktualizacja 9 czerwca 2015 r. - Bardzo ważne

Swift 2.0 wyposażony try, throworaz catchsłów kluczowych i najbardziej ekscytujące jest:

Swift automatycznie tłumaczy metody Objective-C, które powodują błędy, na metody zgłaszające błąd zgodnie z natywną funkcją obsługi błędów Swift.

Uwaga: Metody, które zużywają błędy, takie jak metody delegowania lub metody, które przyjmują procedurę obsługi zakończenia z argumentem obiektu NSError, nie stają się metodami rzucającymi się po zaimportowaniu przez Swift.

Fragment: Apple Inc. „Używanie Swift z kakao i Objective-C (wstępna wersja Swift 2)”. iBooks.

Przykład: (z książki)

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);
}

Odpowiednikiem w swift będzie:

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)")
}

Zgłaszanie błędu:

*errorPtr = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotOpenFile userInfo: nil]

Zostaną automatycznie propagowane do dzwoniącego:

throw NSError(domain: NSURLErrorDomain, code: NSURLErrorCannotOpenFile, userInfo: nil)

Z książek Apple, The Swift Programming Language, wydaje się, że błędy powinny być rozwiązywane za pomocą enum.

Oto przykład z książki.

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)"
}

Od: Apple Inc. „Swift Programming Language”. iBooks. https://itun.es/br/jEUH0.l

Aktualizacja

Z książek prasowych Apple „Używanie Swift z kakao i Objective-C”. Wyjątki czasu wykonywania nie występują przy użyciu szybkich języków, dlatego nie masz opcji catch-catch. Zamiast tego używasz opcjonalnego łączenia .

Oto fragment książki:

Na przykład w poniższym wykazie kodu pierwszy i drugi wiersz nie są wykonywane, ponieważ właściwość length i metoda characterAtIndex: nie istnieją w obiekcie NSDate. Stała myLength jest wywnioskowana jako opcjonalna wartość Int i jest ustawiona na zero. Możesz także użyć instrukcji if – let, aby warunkowo rozpakować wynik metody, na którą obiekt może nie odpowiedzieć, jak pokazano w trzecim wierszu

let myLength = myObject.length?
let myChar = myObject.characterAtIndex?(5)
if let fifthCharacter = myObject.characterAtIndex(5) {
    println("Found \(fifthCharacter) at index 5")
}

Fragment: Apple Inc. „Używanie Swift z kakao i Objective-C”. iBooks. https://itun.es/br/1u3-0.l


Książki zachęcają również do korzystania ze wzoru błędu kakao z Objective-C (NSError Object)

Raportowanie błędów w Swift odbywa się według tego samego wzoru, co w Celu C, z dodatkową korzyścią oferowania opcjonalnych wartości zwrotu. W najprostszym przypadku zwraca się wartość Bool z funkcji, aby wskazać, czy się udało. Gdy musisz zgłosić przyczynę błędu, możesz dodać do funkcji parametr wyjścia NSError typu NSErrorPointer. Ten typ jest w przybliżeniu równoważny NSErrorowi Objective-C **, z dodatkowym bezpieczeństwem pamięci i opcjonalnym pisaniem. Przedrostka i operatora można użyć do przekazania odwołania do opcjonalnego typu NSError jako obiektu NSErrorPointer, jak pokazano na poniższej liście kodów.

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)")
    }
}

Fragment: Apple Inc. „Używanie Swift z kakao i Objective-C”. iBooks. https://itun.es/br/1u3-0.l


Ostatnia instrukcja powinna wyglądać następująco: do {try myString.writeToFile (ścieżka, atomowo: true, kodowanie: NSUTF8StringEncoding)} catch let error jako NSError {print (błąd)}
Jacky

1
@Jacky Tak, tak jest w przypadku swift 2.0, chociaż ta odpowiedź została napisana przed wydaniem swift 2.0, zaktualizowałem odpowiedź, aby pokazać nowy sposób obsługi błędów w swift 2.0. Zastanawiałem się nad tym w celach informacyjnych, ale rozważę aktualizację całej odpowiedzi, aby używać tylko szybkiego 2.0
Guilherme Torres Castro

12

W Swift nie ma wyjątków, podobnych do podejścia Celu C.

W fazie projektowania można użyć assertdo wychwycenia wszelkich błędów, które mogą się pojawić, i należy je naprawić przed przejściem do produkcji.

Klasyczne NSErrorpodejście się nie zmienia, wysyłasz wiadomość NSErrorPointer, która zostaje zapełniona.

Krótki przykład:

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)")
}

6
Rodzi to dwa pytania: co dzieje się, gdy kod ObjC, który wywołujemy z Swift, faktycznie zgłasza wyjątek i czy NSError jest naszym uniwersalnym obiektem błędu, jak w ObjC?
MDJ

1
Czy to tylko fakt z życia w Swift, że inicjalizatory nie działają lub nie mogą zawieść?
Phil

11
Obsługa wyjątków wydaje się raczej brudna
Tash Pemhiwa

27
Tak, kto potrzebuje wyjątków, kiedy możesz po prostu upaść? Lub wstaw NSError ** jako argument we wszystkich zadeklarowanych funkcjach? tak, że każdy f();g();staje się f(&err);if(err) return;g(&err);if(err) return;przez pierwszy miesiąc, a potem staje sięf(nil);g(nil);hopeToGetHereAlive();
hariseldon78

2
Ta odpowiedź jest zarówno nieaktualna (Swift obsługuje teraz wyjątki), jak i błędna (Cel C obsługuje wyjątki.
Rog

11

Zalecany „Swift Way” to:

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
}

Jednak wolę try / catch, ponieważ łatwiej jest mi naśladować, ponieważ przesuwa on obsługę błędów do osobnego bloku na końcu, takie ustawienie jest czasem nazywane „Złotą Ścieżką”. Na szczęście możesz to zrobić za pomocą zamknięć:

TryBool {
    write("~/Error2")(error: $0) // The code to try
}.catch {
    println("write failure 2: \($0!.localizedDescription)") // Report failure
    // assert(false) // Terminate program
}

Łatwo jest również dodać funkcję ponawiania:

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
}

Lista dla TryBool to:

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)
    }
}

Możesz napisać podobną klasę do testowania opcjonalnej wartości zwracanej zamiast wartości Bool:

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)
    }
}

Wersja TryOptional wymusza nie opcjonalny typ zwrotu, który ułatwia późniejsze programowanie, np. „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

Korzystanie z 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
}

Uwaga automatyczne rozpakowywanie.


7

Edycja: Chociaż ta odpowiedź działa, jest to niewiele więcej niż Cel-C transliterowany na Swift. Został on przestarzały przez zmiany w Swift 2.0. Powyższa odpowiedź Guilherme Torres Castro stanowi bardzo dobre wprowadzenie do preferowanego sposobu obsługi błędów w Swift. VOS

Trochę to rozgryzłem, ale myślę, że to zrobiłem. Wydaje się to brzydkie. Nic więcej niż cienka skóra nad wersją Objective-C.

Wywoływanie funkcji z parametrem 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
}`

Zapisywanie funkcji, która przyjmuje parametr błędu ...

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...
}


5

Podstawowe opakowanie wokół celu C, które daje funkcję try catch. https://github.com/williamFalcon/SwiftTryCatch

Użyj jak:

SwiftTryCatch.try({ () -> Void in
        //try something
     }, catch: { (error) -> Void in
        //handle error
     }, finally: { () -> Void in
        //close resources
})

Dobry pomysł. Ale kto decyduje się na to, musi pamiętać, że obiekty przydzielone w bloku try nie są zwalniane po zgłoszeniu wyjątku. Może to powodować problemy z obiektami zombie i każde użycie RAII jest zagrożone (automatyczne odblokowywanie, automatyczne zatwierdzanie sql, automatyczne wycofywanie sql ...). Może c ++ może nam pomóc z jakąś formą „runAtExit”?
hariseldon78

Aktualizacja: właśnie odkryłem, że jest flaga w clang, aby umożliwić zwolnienie obiektów przy rzucaniu wyjątków: -fobjc-arc-wyjątki. Muszę spróbować, jeśli nadal działa z opakowaną wersją (myślę, że powinno)
hariseldon78

Jeśli skorzystasz z tej opcji, pamiętaj, że rozmiar kodu wzrasta, ponieważ kompilator musi wygenerować kod częściowo bezpieczny dla wyjątków. Ponadto: Poleganie na takiej funkcji kompilatora może nie być najlepszym pomysłem. Wyjątkiem są tylko błędy programistów, dlatego nie warto tego robić na tej opcji kompilatora, aby zaoszczędzić trochę pamięci podczas programowania. Jeśli masz wyjątki w kodzie produkcyjnym, powinieneś zająć się sprawą powodującą te wyjątki w pierwszej kolejności.
Christian Kienle,

1
Mogą zdarzyć się sytuacje poza twoją kontrolą. Na przykład parsowanie JSON w niewłaściwym formacie.
William Falcon

3

To jest odpowiedź na aktualizację dla Swift 2.0. Nie mogę się doczekać bogatego w funkcje modelu obsługi błędów jak w Javie. W końcu ogłosili dobre wieści. tutaj

Model obsługi błędów: nowy model obsługi błędów w Swift 2.0 będzie od razu wydawał się naturalny, ze znanymi słowami kluczowymi „spróbuj, rzucaj i łap” . Najlepsze jest to, że został zaprojektowany do współpracy z zestawami Apple SDK i NSError. W rzeczywistości NSError jest zgodny z typem błędu Swift. Na pewno będziesz chciał obejrzeć sesję WWDC w sekcji Co nowego w Swift, aby dowiedzieć się więcej na ten temat.

np .:

func loadData() throws { }
func test() {
do {
    try loadData()
} catch {
    print(error)
}}

3

Jak powiedział Guilherme Torres Castro, w Swift 2.0, try, catch,do mogą być wykorzystywane w programowaniu.

Na przykład, w CoreData metodę pobierania danych, zamiast umieścić &errorjako parametr do managedContext.executeFetchRequest(fetchRequest, error: &error), teraz musimy tylko chcesz używać managedContext.executeFetchRequest(fetchRequest), a następnie z obsługi błędu try, catch( jabłko Document Link )

do {
   let fetchedResults = try managedContext.executeFetchRequest(fetchRequest) as? [NSManagedObject]
   if let results = fetchedResults{
      people = results
   }
} catch {
   print("Could not fetch")
}

Jeśli już pobrałeś xcode7 Beta. Spróbuj wyszukać błędy związane z rzucaniem w dokumentacji i dokumentacji API i wybierz pierwszy pokazujący wynik, daje to podstawowe wyobrażenie o tym, co można zrobić dla tej nowej składni. Jednak pełna dokumentacja nie jest jeszcze opublikowana dla wielu interfejsów API.

Bardziej wymyślne techniki obsługi błędów można znaleźć w

Co nowego w Swift (sesja 2015 2015 28 2830)



1

Ładna i prosta biblioteka do obsługi wyjątku: TryCatchFinally-Swift

Podobnie jak kilka innych, obejmuje on funkcje celu wyjątku C.

Użyj tego w ten sposób:

try {
    println("  try")
}.catch { e in
    println("  catch")
}.finally {
    println("  finally")
}

Dodałem próbkę :)
Morten Holmgaard,

Prawdopodobnie warto wspomnieć o opinii autorów: „Ostrzeżenie: to hack dla zabawy i zła. Oprzyj się pokusie, aby z niego skorzystać”.
jbat100

1

Począwszy od Swift 2, jak już wspomnieli inni, obsługa błędów najlepiej realizować za pomocą wyliczeń do / try / catch i ErrorType. Działa to całkiem dobrze w przypadku metod synchronicznych, ale do obsługi błędów asynchronicznych wymagana jest spryt.

Ten artykuł ma świetne podejście do tego problemu:

https://jeremywsherman.com/blog/2015/06/17/using-swift-throws-with-completion-callbacks/

Podsumowując:

// 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()
    }

wówczas wywołanie powyższej metody wyglądałoby następująco:

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
            }
        })

Wydaje się to nieco czystsze niż oddzielne wywołanie zwrotne errorHandler przekazane do funkcji asynchronicznej, tak było to obsługiwane przed wersją Swift 2.


0

Widziałem, że ze względu na naturę urządzenia nie chcesz rzucać użytkownikowi wielu tajemniczych komunikatów o błędach. Dlatego większość funkcji zwraca wartości opcjonalne, po prostu kodujesz, aby zignorować opcjonalne. Jeśli funkcja wróci zero, co oznacza, że ​​się nie powiodła, możesz wstawić wiadomość lub cokolwiek innego.


1
Zwrócenie zera nie zwraca żadnych informacji o charakterze błędu. Jeśli obiekt błędu zostanie zwrócony, gdy wystąpi błąd, wówczas, w zależności od błędu, programiści mogą zignorować go, obsłużyć go, pozwolić, aby pojawił się bąbelek lub „pop wiadomość” lub cokolwiek innego. Wiedza to potęga.
Vince O'Sullivan,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.