spróbuj, spróbuj! & próbować? jaka jest różnica i kiedy ich używać?

172

W Swift 2.0 firma Apple wprowadziła nowy sposób obsługi błędów (spróbuj złapać). Kilka dni temu w Beta 6 zostało wprowadzone jeszcze nowsze słowo kluczowe ( try?). Wiedziałem też, że mogę użyć try!. Jaka jest różnica między trzema słowami kluczowymi i kiedy ich używać?

Abdurrahman
źródło

Odpowiedzi:

316

Zaktualizowano dla Swift 5.1

Załóżmy, że funkcja rzucania:

enum ThrowableError: Error {

    case badError(howBad: Int)
}

func doSomething(everythingIsFine: Bool = false) throws -> String {

  if everythingIsFine {
      return "Everything is ok"
  } else {
      throw ThrowableError.badError(howBad: 4)
  }
}

próbować

Masz 2 opcje, gdy próbujesz wywołać funkcję, która może rzucać.

Możesz wziąć odpowiedzialność za obsługę błędów , otaczając połączenie w bloku do-catch:

do {
    let result = try doSomething()
}
catch ThrowableError.badError(let howBad) {
    // Here you know about the error
    // Feel free to handle or to re-throw

    // 1. Handle
    print("Bad Error (How Bad Level: \(howBad)")

    // 2. Re-throw
    throw ThrowableError.badError(howBad: howBad)
}

Lub po prostu spróbuj wywołać funkcję i przekaż błąd do następnego wywołującego w łańcuchu wywołań:

func doSomeOtherThing() throws -> Void {    
    // Not within a do-catch block.
    // Any errors will be re-thrown to callers.
    let result = try doSomething()
}

próbować!

Co się dzieje, gdy próbujesz uzyskać dostęp do niejawnie rozpakowanego elementu opcjonalnego z zerem w środku? Tak, to prawda, aplikacja ulegnie awarii! To samo dotyczy try! zasadniczo ignoruje łańcuch błędów i deklaruje sytuację „zrób albo zgiń”. Jeśli wywołana funkcja nie zgłosiła żadnych błędów, wszystko idzie dobrze. Ale jeśli się nie powiedzie i wyrzuci błąd, aplikacja po prostu ulegnie awarii .

let result = try! doSomething() // if an error was thrown, CRASH!

próbować?

Nowe słowo kluczowe, które zostało wprowadzone w Xcode 7 beta 6. Zwraca opcjonalne, które rozpakowuje pomyślne wartości i wyłapuje błąd, zwracając nil.

if let result = try? doSomething() {
    // doSomething succeeded, and result is unwrapped.
} else {
    // Ouch, doSomething() threw an error.
}

Lub możemy użyć strażnika:

guard let result = try? doSomething() else {
    // Ouch, doSomething() threw an error.
}
// doSomething succeeded, and result is unwrapped.

Ostatnia uwaga tutaj, używając try?uwagi, że odrzucasz błąd, który miał miejsce, ponieważ jest on tłumaczony na zero. Użyj try? kiedy koncentrujesz się bardziej na sukcesach i porażkach, a nie na tym, dlaczego coś zawiodło.

Korzystanie z operatora koalescencji?

Możesz użyć operatora koalescencji ?? z próbą? aby podać wartość domyślną w przypadku awarii:

let result = (try? doSomething()) ?? "Default Value"
print(result) // Default Value
Abdurrahman
źródło
Twój drugi przykładowy kod ( let result = try doSomething() // Not within a do-catch block) ma zostać wywołany z metody zadeklarowanej jako throws, prawda? Więc jeśli doSomething()zawiedzie, to czy też metoda zewnętrzna (z kolei)?
Nicolas Miari
Stary wątek i wszystko, ale znalazłem, że dzisiaj (Swift 4, Xcode 9.1) spróbować? nie rozpakowuje wyniku automatycznie. Po prostu pozostawia to jako normalną opcję ręcznego rozpakowania. Nie jestem pewien, czy zmieniło się to od czasu Swift 2/3, ale jest to zgodne z dokumentacją: developer.apple.com/library/content/documentation/Swift/ ... (zobacz Konwersja błędów na wartości opcjonalne ). Świetne wyjaśnienie próby przy okazji.
the_dude_abides
1
w Swift 4, spróbować? nie usuwa wywołań no funkcji rzucania występujących w wyrażeniach „try” w moim projekcie.
aznelite89
7
Możesz również użyć try?with, ??aby umożliwić zdefiniowanie wartości domyślnej w jednej linii:let something:String = (try? whateverIfItThrows()) ?? "Your default value here"
itMaxence