Technologische Neuigkeiten, Bewertungen und Tipps!

Fehlerbehandlung mit Try and Catch in Swift

Hinweis: Der folgende Artikel hilft Ihnen weiter: Fehlerbehandlung mit Try and Catch in Swift

Zurück zum Blog

Sie verwenden „do“, „try“, „catch“ und „throw“, um Fehler und Ausnahmen in Swift zu behandeln. Durch die Fehlerbehandlung haben Sie eine bessere Kontrolle über unerwartete Szenarien, die in Ihrem Code auftreten können, z. B. wenn ein Benutzer ein falsches Kontokennwort eingibt.

In diesem Tutorial besprechen wir Folgendes:

  • Warum es wichtig ist, Fehler zu fangen (und zu werfen).
  • Syntax zur Behandlung von Fehlern in Swift, mit „Do Try Catch“.
  • Wie man Fehler und Ausnahmen auslöst und erneut auslöst
  • So erstellen Sie Ihre eigenen Fehlertypen (und warum)
  • Wann sollten Fehler mit try in optionale Werte konvertiert werden?
  • Arbeiten mit try! Fehler (und ihre Risiken) deaktivieren

Einige Fehler und Bugs in Ihrer App sind das Ergebnis eines Fehlers des Programmierers. Wenn Ihre App beispielsweise abstürzt und der Index außerhalb des zulässigen Bereichs liegt, haben Sie wahrscheinlich irgendwo einen Tippfehler gemacht. Wenn Sie das Auspacken einer Option erzwingen, die Null ist, stürzt Ihre App unweigerlich ab.

Im Idealfall weist Ihre App keine dieser Fehler auf. Ihre Aufgabe als Programmierer besteht darin, sie zu finden und zu beheben, bevor Ihre App im App Store veröffentlicht wird. In diesem Tutorial werden wir über eine andere Art von Fehler sprechen.

In der praktischen iOS-Entwicklung sind nicht alle Fehler schlecht. Einige Fehler sind Teil des Lebenszyklus einer App, z. B. die Ausnahme „Falsches Passwort“, wenn Sie versehentlich versuchen, sich mit den falschen Kontoanmeldeinformationen anzumelden.

Fehler wie diese sind behebbar. Sie möchten den Fehler erkennen und entsprechend reagieren, indem Sie dem Benutzer beispielsweise einen Warndialog anzeigen, der ihn über den Fehler informiert.

Ein paar Beispiele:

  • Ein Geldautomat zeigt „Falscher PIN-Code“ an, wenn Sie versuchen, Geld abzuheben
  • Wenn Sie versuchen, den Motor zu starten, zeigt Ihr Auto die Anzeige „Kraftstoffstand niedrig“ an
  • Ein Authentifizierungsversuch für eine API gibt „Falscher Benutzername/Passwort“ zurück.

Sie können diese Fehler beheben, indem Sie eine Warnmeldung anzeigen oder etwas anderes tun. Beispielsweise kann es nach drei Fehlversuchen zu einer Sperrung Ihrer Kreditkarte kommen. Ihr Auto kann Sie zur nächsten Tankstelle führen, wenn Sie kein Benzin mehr haben. Und Sie können versuchen, sich mit einem anderen Benutzernamen und Passwort anzumelden.

Hierbei ist zu beachten, dass diese Fehler nicht zu einem Absturz führen, der zum Beenden der App führt. Es wäre schade, wenn ein Geldautomat neu gestartet werden müsste, wenn Sie den falschen PIN-Code eingegeben hätten! Diese Unterscheidung ist wichtig; zwischen behebbaren Fehlern und tatsächlichen Fehlern, die behoben werden müssen.

Wie Sie bald erfahren werden, behandeln Sie Fehler in Swift mit den Schlüsselwörtern „do“, „try“, „catch“ und „throw“ sowie mit dem nativen Fehlertyp von Swift. Swift bietet erstklassige Unterstützung für den Umgang mit Fehlern, was bedeutet, dass es auf Sprachebene implementiert ist und über die gleiche Art von Kontrolle verfügt wie beispielsweise „If“, „Guard“ und „Return“.

Lass uns weitermachen!

Bevor wir uns mit der Fehlerbehandlung befassen, werfen wir zunächst einen Blick auf deren Auslösung. Das passiert mit dem Schlüsselwort throw in Swift. Jeder Fehler oder jede Ausnahme „startet“, wenn sie ausgelöst wird.

Schauen Sie sich das an:

if Fuel < 999 { throw RocketError.insufficientFuel } Im obigen Code wird das Schlüsselwort throw verwendet, um einen Fehler vom Typ RocketError.insufficientFuel auszulösen, wenn die Kraftstoffvariable kleiner als 999 ist. Mit anderen Worten, wir geben diesen Fehler aus, wenn: Beim Ausführen des Codes kommt es vor, dass nicht genügend Kraftstoff vorhanden ist. Durch das Auslösen eines Fehlers können Sie darauf hinweisen, dass etwas Unerwartetes passiert ist. Als Sie diesen Code in Ihrer App geschrieben haben, haben Sie damit gerechnet, dass diese Ausnahme auftreten könnte, weshalb Sie dort diesen bedingten und werfenden Code eingefügt haben. Stellen Sie sich vor, wir versuchen, eine Rakete zu starten. So: func igniteRockets(fuel: Int, astronauts: Int) throws { if Fuel < 999 { throw RocketError.insufficientFuel } else if astronauts < 3 { throw RocketError.insufficientAstronauts(needed: 3) } // Raketen zünden print("3 ... 2... 1... IGNITION!!! LIFTOFF!!!") } Die Funktion igniteRockets(fuel:astronauts:) zündet die Raketen nur, wenn der Treibstoff größer oder gleich 999 ist und wenn es mindestens 3 Astronauten an Bord. Im obigen Code verwenden wir einen Fehlertyp namens RocketError. So sieht es aus: enum RocketError: Error { case independentFuel case unlimitedAstronauts(needed: Int) case unlimitedError } Diese Enumeration erweitert das Error-Protokoll. Es definiert drei Arten von Fehlern: .insufficientFuel, independentAstronauts(needed) und .unknownError. Sie können throw nur mit einem Wert verwenden, der durch den Typ „Error“ dargestellt wird. Das Definieren eigener Fehlertypen ist äußerst nützlich, da Sie sich darüber im Klaren sein können, was diese Fehler in Ihrem Code bedeuten. Nehmen wir zum Beispiel den Fehler „insuffizientAstronauts(needed:)“. Wenn dieser Fehler ausgegeben wird (siehe Code oben), können Sie ein Argument angeben, das angibt, wie viele Astronauten erforderlich sind, um die Rakete erfolgreich zu zünden. Sauber! Das Schlüsselwort throw hat die gleiche Wirkung wie das Schlüsselwort return. Wenn throw aufgerufen wird, stoppt die Ausführung der Funktion an diesem Punkt und der ausgelöste Fehler wird an den Aufrufer der Funktion übergeben. An diesem Punkt muss es mit einem Do-Try-Catch-Block behandelt werden, den wir im nächsten Abschnitt besprechen werden. Die große Frage ist natürlich, ob Sie die gleiche Funktionalität implementieren können, ohne Throw zu verwenden. Zum Beispiel so: func igniteRockets(fuel: Int, astronauts: Int) { if Fuel >= 999 && astronauts >= 3 {
print(“3… 2… 1… ZÜNDUNG!!! ABHEBEN!!!“)
} anders {
// … Was ist zu tun!?
}
}

Ist Ihnen der Unterschied zur vorherigen Funktion aufgefallen? Dies ist eine architektonische Frage, über die es sich zu denken lohnt.

Die Fehlerbehandlung mit „Throw“, „Try“ und „Catch“ ist ein Tool, mit dem Sie Ihren Code leichter lesbar, wartungsfähig und erweiterbar machen können. Ihnen stehen einige Alternativen zur Verfügung, der Teufel steckt also – wie bei allem beim Codieren – im Detail. Welches Tool eignet sich am besten für Sie?

Eine letzte Sache: Die Funktion igniteRockets() ist mit dem Schlüsselwort throws gekennzeichnet. Es zeigt jedem, der diese Funktion aufruft, an, dass Fehler behandelt werden müssen. Swift zwingt uns, mit Fehlern umzugehen (oder sie erneut auszulösen), was bedeutet, dass Sie es nicht versehentlich vergessen können! Diese Funktion macht die Verwendung von Swift sicherer und verursacht weniger Fehler, da diese schneller erkannt werden.

Apropos Umgang mit Fehlern, das besprechen wir als Nächstes!

In Swift behandeln Sie Fehler mit einem Do-Try-Catch-Block, um bei Auftreten eines Fehlers entsprechende Maßnahmen zu ergreifen.

Hier ist ein Beispiel:

Tun {
Versuchen Sie es mit IgniteRockets (Treibstoff: 5000, Astronauten: 1)
} fangen {
drucken(Fehler)
}
Der Umgang mit Fehlern mit do try Catch hat drei wichtige Aspekte:

  1. Stellen Sie der Funktion (oder dem Ausdruck), die auslösen kann, das Schlüsselwort try voran
  2. Wickeln Sie den Code, der try hat, in einen do { }-Block ein
  3. Hängen Sie einen oder mehrere Catch { }-Blöcke an, um alle oder einzelne Fehler zu behandeln

In vielen anderen Programmiersprachen wie Java oder C++ wird dieser Ansatz zur Fehlerbehandlung als Try/Catch (ohne Do) bezeichnet. Der Ausdruck, der Fehler auslösen kann, ist nicht explizit mit try gekennzeichnet. Swift macht dies deutlich.

Wenn Sie sich den obigen Code ansehen, können Sie sich vorstellen, was als nächstes passiert. Die Funktion igniteRockets() gibt einen Fehler aus und anschließend wird der Catch-Block aufgerufen. Dies gibt den Fehler mit print(error) aus.

Gut zu wissen ist, dass dieser Wert implizit verfügbar ist, auch wenn im Catch-Block keine Konstante mit dem Namen error deklariert wurde. Innerhalb eines Catch-Blocks können Sie die Fehlerkonstante verwenden, um den ausgegebenen Fehler abzurufen.

Sie können den Fehlerwert auch explizit deklarieren, etwa so:

Tun {

} Catch(Let-Ausnahme) {
print(Exception.localizedDescription)
}

Mit do-try-catch können Sie auch individuell auf Fehlerfälle reagieren. Erinnern Sie sich an die RocketError-Enumeration, die wir zuvor definiert haben? Schauen Sie sich das jetzt an:

Tun {
Versuchen Sie es mit IgniteRockets (Treibstoff: 5000, Astronauten: 1)
} Catch RocketError.insufficientFuel {
print(„Die Rakete braucht mehr Treibstoff zum Abheben!“)
} Catch RocketError.insufficientAstronauts(let needed) {
print(„Sie benötigen mindestens \(benötigte) Astronauten…“)
}

Die oben genannten Catch-Blöcke werden basierend auf Aufzählungsfällen von RocketError aufgerufen. Seine Syntax ähnelt der eines Schalterblocks. Sie können direkt auf zugehörige Werte von Enum-Fällen zugreifen, beispielsweise auf die benötigte Konstante im obigen Beispiel.

Sie können auch Ausdrücke und Mustervergleiche verwenden, um mehr Kontrolle über den Catch-Block zu erhalten, der bei einem Fehler ausgelöst wird.

Warum probieren Sie es nicht selbst aus?

Aufzählung RocketError: Fehler {
Fall unzureichendKraftstoff
Fall nicht ausreichendAstronauts(benötigt: Int)
Fall unbekanntError
}

func igniteRockets(fuel: Int, astronauts: Int) wirft
{
if Treibstoff < 999 { throw RocketError.insufficientFuel } else if astronauts < 3 { throw RocketError.insufficientAstronauts(needed: 3) } // Raketen zünden print("3... 2... 1... IGNITION!!! LIFTOFF !!!") } do { try igniteRockets(fuel: 5000, astronauts: 1) } Catch { print(error) }Kurzer Tipp: Sie können eine Textdarstellung eines Fehlers bereitstellen, indem Sie eine Erweiterung für Ihren benutzerdefinierten Fehlertyp erstellen, die dem LocalizedError-Protokoll entspricht. Verwenden Sie die Erweiterung, um die Eigenschaften „errorDescription“, „failureReason“, „helpAnchor“ und „recoverySuggestion“ zu implementieren. Durchlaufen Sie die Fehleraufzählung und geben Sie Zeichenfolgenwerte zurück, die den Fehler beschreiben.

Der Zweck der Fehlerbehandlung besteht darin, explizit zu bestimmen, was passiert, wenn ein Fehler auftritt. Dadurch können Sie Fehler beheben, anstatt die App einfach abstürzen zu lassen.

In manchen Fällen ist Ihnen der Fehler selbst egal. Sie möchten beispielsweise nur einen Wert von einer Funktion erhalten. Und wenn ein Fehler auftritt, ist die Rückgabe von Null kein Problem.

Kann man das mit dem Versuch machen? Stichwort. Es wandelt jeden auftretenden Fehler in einen optionalen Wert um. Die Syntax kombiniert try mit einem Fragezeichen ?, ähnlich wie bei der Arbeit mit optionalen Zeichen. Wenn Sie try? verwenden, müssen Sie nicht den vollständigen Do-Try-Catch-Block verwenden.

Hier ist ein Beispiel:

Ergebnis zulassen = versuchen? berechneWert(für: 42)

Stellen Sie sich vor, dass die Funktion „calculateValue(for:)“ Fehler auslösen kann, beispielsweise wenn ihr Parameter ungültig ist. Anstatt diesen Fehler mit do-try-catch zu behandeln, konvertieren wir den zurückgegebenen Wert in einen optionalen Wert.

Eines von zwei Dingen wird nun passieren:

  1. Gibt keinen Fehler aus: Die Funktion gibt einen Wert zurück, der dem Ergebnis zugewiesen wird
  2. Löst einen Fehler aus: Es wird kein Wert zurückgegeben, daher ist das Ergebnis Null

Wenn Sie Fehler auf diese Weise behandeln, können Sie von der Syntax profitieren, die für Optionen spezifisch ist, wie z. B. den Null-Koaleszier-Operator ?? und optionaler Bindung. So was:

Wenn let result = try? berechneWert(für: 99) {
// Etwas mit dem nicht optionalen Wert „result“ tun
}

Mit Null-Koaleszenz-Operator ?? um einen Standardwert anzugeben:

Ergebnis zulassen = versuchen? berechneWert(für: 123) ?? 9000

Seien Sie vorsichtig bei der Verwendung von try? zu oft. Es ist zwingend, try? um Fehler zu ignorieren oder zum Schweigen zu bringen. Gewöhnen Sie sich also nicht an, try? um unerwartete Fehler zu vermeiden.

Die Fehlerbehandlung mit do-try-catch ist aus gutem Grund eine Funktion, denn sie macht Ihren Code im Allgemeinen sicherer und weniger fehleranfällig. Das Vermeiden der Wiederherstellung nach Fehlern führt später nur zu (stillen) Fehlern.

Was ist, wenn Sie eine Funktion programmieren, die einen Aufruf einer anderen Funktion enthält, die Fehler auslösen kann, Sie sich aber nicht direkt mit dem Fehler befassen möchten? Markieren Sie Ihre eigene Funktion mit Wiederholungswürfen. Wie der Name schon sagt, wird der Fehler dadurch „erneut“ an den Aufrufer Ihrer Funktion weitergeleitet.

Mit try! können Sie die Fehlerbehandlung auch vollständig deaktivieren. Dadurch wird die Ausbreitung des Fehlers effektiv gestoppt – und Ihre App stürzt ab.

Genauso wie der Versuch? Stichwort: der Versuch! Die Syntax kombiniert try mit einem Ausrufezeichen !, auf ähnliche Weise, um das Auspacken von Optionen zu erzwingen. Wenn Sie try! verwenden, müssen Sie nicht den vollständigen Do-Try-Catch-Block verwenden.

Im Gegensatz zu try?, das einen optionalen Wert zurückgibt, ist try! Die Syntax führt zum Absturz Ihres Codes, wenn ein Fehler auftritt. Es gibt zwei verschiedene Szenarien, in denen dies nützlich ist:

  1. Probieren Sie es aus! wenn Ihr Code unmöglich zu einem Fehler führen könnte, dh wenn aus dem Kontext Ihres Codes 100 % sicher ist, dass ein Fehler nicht auftreten kann
  2. Probieren Sie es aus! wenn Sie einen Fehler nicht beheben können und es unmöglich ist, die Ausführung über diesen Punkt hinaus fortzusetzen

Ein paar Beispiele:

  • Stellen Sie sich vor, Sie erstellen eine App, die ein mit der App geliefertes Bildelement enthält. Wenn Sie das Bild von der Festplatte laden, verwenden Sie eine Funktion, die einen Fehler auslöst, wenn die Bilddatei nicht vorhanden ist. Da Sie zu 100 % sicher sind, dass das Bild mit der App geliefert wird, können Sie try! bedenkenlos verwenden. um das Bild zu laden – da der Fehler „fehlende Datei“ nie ausgegeben wird.
  • Stellen Sie sich vor, Sie programmieren eine App, in die eine Datenbankdatei eingebettet ist. Die Funktion zum Laden der Datenbank löst einen Fehler aus, wenn die Datenbank beschädigt ist. Sie können try! sicher verwenden. um diese Datenbank in den Speicher zu laden, denn wenn die Datenbank beschädigt ist, ist die App ohnehin nicht verwendbar.

Der Umgang mit Fehlern mit do-try-catch hilft Ihnen, robusteren und fehlertoleranteren Code zu schreiben. Sie können damit Fehler beheben, indem Sie beispielsweise dem Benutzer eine Fehlermeldung anzeigen.

Mit einer Syntax wie try? und versuche! Sie können prägnanteren Code schreiben. Und benutzerdefinierte Fehlertypen geben Ihnen die Möglichkeit, mitzuteilen, welche Art von Fehler aufgetreten ist, und entsprechend zu reagieren. Sauber!

Table of Contents