Diese Antwort ist Community-Wiki . Wenn Sie der Meinung sind, dass es besser gemacht werden könnte, können Sie es jederzeit bearbeiten !
Hintergrund: Was ist optional?
In Swift Optional
ist dies ein generischer Typ , der einen Wert (jeglicher Art) oder überhaupt keinen Wert enthalten kann.
In vielen anderen Programmiersprachen wird häufig ein bestimmter "Sentinel" -Wert verwendet, um auf das Fehlen eines Werts hinzuweisen . In Objective-C zeigt beispielsweise nil
(der Nullzeiger ) das Fehlen eines Objekts an. Bei der Arbeit mit primitiven Typen wird dies jedoch schwieriger. Sollte -1
dies verwendet werden, um das Fehlen einer Ganzzahl oder vielleicht INT_MIN
einer anderen Ganzzahl anzuzeigen ? Wenn ein bestimmter Wert als "keine Ganzzahl" ausgewählt wird, bedeutet dies, dass er nicht mehr als gültiger Wert behandelt werden kann.
Swift ist eine typsichere Sprache, dh die Sprache hilft Ihnen dabei, klar zu machen, mit welchen Arten von Werten Ihr Code arbeiten kann. Wenn ein Teil Ihres Codes einen String erwartet, verhindert die Typensicherheit, dass Sie ihm versehentlich einen Int übergeben.
In Swift kann jeder Typ optional gemacht werden . Ein optionaler Wert kann einen beliebigen Wert des ursprünglichen Typs oder des speziellen Werts annehmen nil
.
Optionale Optionen werden mit einem ?
Suffix für den Typ definiert:
var anInt: Int = 42
var anOptionalInt: Int? = 42
var anotherOptionalInt: Int? // `nil` is the default when no value is provided
Das Fehlen eines Wertes in einem optionalen wird angezeigt durch nil
:
anOptionalInt = nil
(Beachten Sie, dass dies nil
nicht mit dem nil
in Objective-C identisch ist . In Objective-C nil
fehlt ein gültiger Objektzeiger . In Swift sind Optionals nicht auf Objekte / Referenztypen beschränkt. Optional verhält es sich ähnlich wie Haskell's Maybe .)
Warum wurde " schwerwiegender Fehler: Beim Auspacken eines optionalen Werts unerwartet Null gefunden" angezeigt ?
Um auf den Wert eines optionalen Elements zuzugreifen (falls überhaupt vorhanden), müssen Sie ihn auspacken . Ein optionaler Wert kann sicher oder gewaltsam ausgepackt werden. Wenn Sie eine Option zwangsweise auspacken und sie keinen Wert hat, stürzt Ihr Programm mit der obigen Meldung ab.
Xcode zeigt Ihnen den Absturz an, indem Sie eine Codezeile markieren. Das Problem tritt in dieser Zeile auf.
Dieser Absturz kann mit zwei verschiedenen Arten des Force-Unwraps auftreten:
1. Explizite Force-Entpackung
Dies erfolgt mit dem !
Bediener optional. Zum Beispiel:
let anOptionalString: String?
print(anOptionalString!) // <- CRASH
Schwerwiegender Fehler: Beim Auspacken eines optionalen Werts wurde unerwartet Null gefunden
Wie anOptionalString
ist nil
hier, Sie werden einen Absturz auf der Linie, wo Sie unwrap es erzwingen.
2. Implizit ausgepackte Optionen
Diese werden !
eher mit einem als mit einem ?
nach dem Typ definiert.
var optionalDouble: Double! // this value is implicitly unwrapped wherever it's used
Es wird angenommen, dass diese Optionen einen Wert enthalten. Wenn Sie daher auf eine implizit entpackte Option zugreifen, wird diese automatisch für Sie erzwungen. Wenn es keinen Wert enthält, stürzt es ab.
print(optionalDouble) // <- CRASH
Schwerwiegender Fehler: Unerwartet wurde Null gefunden, während implizit ein optionaler Wert entpackt wurde
Um herauszufinden, welche Variable den Absturz verursacht hat, können Sie gedrückt halten, ⌥während Sie auf klicken, um die Definition anzuzeigen, in der Sie möglicherweise den optionalen Typ finden.
Insbesondere IBOutlets sind normalerweise implizit ausgepackte Optionen. Dies liegt daran, dass Ihr xib oder Storyboard die Outlets nach der Initialisierung zur Laufzeit verbindet . Sie sollten daher sicherstellen, dass Sie nicht auf Outlets zugreifen, bevor diese geladen werden. Sie sollten auch überprüfen, ob die Verbindungen in Ihrer Storyboard- / XIB-Datei korrekt sind. Andernfalls werden die Werte nil
zur Laufzeit angezeigt und stürzen daher ab, wenn sie implizit entpackt werden . Versuchen Sie beim Reparieren von Verbindungen, die Codezeilen zu löschen, die Ihre Steckdosen definieren, und verbinden Sie sie dann erneut.
Wann sollte ich jemals ein optionales Auspacken erzwingen?
Explizite Force-Entpackung
In der Regel sollten Sie niemals explizit das Entpacken einer Option mit dem !
Operator erzwingen . Es kann Fälle geben, in denen die Verwendung !
akzeptabel ist. Sie sollten sie jedoch nur verwenden, wenn Sie zu 100% sicher sind, dass die Option einen Wert enthält.
Es kann zwar vorkommen, dass Sie das Entpacken mit Gewalt anwenden können, da Sie wissen , dass ein optionales Element einen Wert enthält - es gibt jedoch keinen einzigen Element Ort, an dem Sie dieses optionale Element nicht sicher auspacken können.
Implizit ausgepackte Optionen
Diese Variablen sind so konzipiert, dass Sie ihre Zuordnung bis zu einem späteren Zeitpunkt in Ihrem Code verschieben können. Es liegt in Ihrer Verantwortung, sicherzustellen, dass sie einen Wert haben, bevor Sie darauf zugreifen. Da es sich jedoch um ein gewaltsames Auspacken handelt, sind sie von Natur aus immer noch unsicher, da davon ausgegangen wird, dass Ihr Wert nicht Null ist, obwohl die Zuweisung von Null gültig ist.
Sie sollten nur implizit entpackte Optionen als letzten Ausweg verwenden . Wenn Sie eine verzögerte Variable verwenden oder einen Standardwert für eine Variable angeben können, sollten Sie dies tun, anstatt eine implizit entpackte Option zu verwenden.
Es gibt jedoch einige Szenarien, in denen implizit ausgepackte Optionen von Vorteil sind , und Sie können weiterhin verschiedene Methoden zum sicheren Auspacken verwenden, wie unten aufgeführt. Sie sollten sie jedoch immer mit der gebotenen Vorsicht verwenden.
Wie kann ich sicher mit Optionals umgehen?
Der einfachste Weg, um zu überprüfen, ob ein optionales Element einen Wert enthält, besteht darin, ihn mit zu vergleichen nil
.
if anOptionalInt != nil {
print("Contains a value!")
} else {
print("Doesn’t contain a value.")
}
In 99,9% der Fälle, in denen Sie mit Optionen arbeiten, möchten Sie tatsächlich auf den darin enthaltenen Wert zugreifen, sofern dieser überhaupt einen enthält. Dazu können Sie die optionale Bindung verwenden .
Optionale Bindung
Mit der optionalen Bindung können Sie überprüfen, ob eine Option einen Wert enthält - und den nicht umschlossenen Wert einer neuen Variablen oder Konstante zuweisen. Es verwendet die Syntax if let x = anOptional {...}
oderif var x = anOptional {...}
, je nachdem, ob Sie den Wert der neuen Variablen nach dem Binden ändern müssen.
Zum Beispiel:
if let number = anOptionalInt {
print("Contains a value! It is \(number)!")
} else {
print("Doesn’t contain a number")
}
Überprüfen Sie zunächst, ob das optionale Element einen Wert enthält. Wenn dies der Fall ist , wird der Wert "entpackt" einer neuen Variablen ( number
) zugewiesen, die Sie dann frei verwenden können, als wäre sie nicht optional. Wenn das optionale Element keinen Wert enthält, wird die else-Klausel wie erwartet aufgerufen.
Das Schöne an der optionalen Bindung ist, dass Sie mehrere Optionen gleichzeitig auspacken können. Sie können die Anweisungen einfach durch ein Komma trennen. Die Anweisung ist erfolgreich, wenn alle Optionen ausgepackt wurden.
var anOptionalInt : Int?
var anOptionalString : String?
if let number = anOptionalInt, let text = anOptionalString {
print("anOptionalInt contains a value: \(number). And so does anOptionalString, it’s: \(text)")
} else {
print("One or more of the optionals don’t contain a value")
}
Ein weiterer guter Trick ist, dass Sie auch Kommas verwenden können, um nach dem Auspacken nach einer bestimmten Bedingung für den Wert zu suchen.
if let number = anOptionalInt, number > 0 {
print("anOptionalInt contains a value: \(number), and it’s greater than zero!")
}
Der einzige Haken bei der Verwendung der optionalen Bindung in einer if-Anweisung besteht darin, dass Sie nur innerhalb des Bereichs der Anweisung auf den nicht umschlossenen Wert zugreifen können. Wenn Sie außerhalb des Gültigkeitsbereichs der Anweisung Zugriff auf den Wert benötigen, können Sie eine Guard-Anweisung verwenden .
Mit einer Guard-Anweisung können Sie eine Bedingung für den Erfolg definieren. Der aktuelle Bereich wird nur dann weiter ausgeführt, wenn diese Bedingung erfüllt ist. Sie werden mit der Syntax definiert guard condition else {...}
.
Um sie mit einer optionalen Bindung zu verwenden, können Sie Folgendes tun:
guard let number = anOptionalInt else {
return
}
(Beachten Sie, dass innerhalb des Schutzkörpers, Sie müssen eine der Verwendung Steuertransferanweisungen , um den Umfang des aktuell ausgeführten Code zu verlassen).
Wenn es anOptionalInt
einen Wert enthält, wird dieser entpackt und der neuen number
Konstante zugewiesen . Der Code nach dem Guard wird dann weiter ausgeführt. Wenn es keinen Wert enthält, führt der Guard den Code in den Klammern aus, was zur Übertragung der Kontrolle führt, sodass der Code unmittelbar danach nicht ausgeführt wird.
Das Schöne an Guard-Anweisungen ist, dass der nicht umschlossene Wert jetzt in Code verwendet werden kann, der auf die Anweisung folgt (da wir wissen, dass zukünftiger Code nur ausgeführt werden kann , wenn das optionale einen Wert hat). Dies ist ideal, um "Pyramiden des Untergangs" zu beseitigen. die durch das Verschachteln mehrerer if-Anweisungen entstehen.
Zum Beispiel:
guard let number = anOptionalInt else {
return
}
print("anOptionalInt contains a value, and it’s: \(number)!")
Guards unterstützen auch dieselben netten Tricks wie die if-Anweisung, z. B. das gleichzeitige Auspacken mehrerer Optionen und die Verwendung der where
Klausel.
Egal , ob Sie eine if oder Schutzerklärung verwenden , hängt vollständig ab , ob ein künftiges Code erfordert die optionalen Wert enthalten.
Null-Koaleszenz-Operator
Der Nil Coalescing Operator ist eine raffinierte Kurzversion des ternären bedingten Operators , der hauptsächlich zum Konvertieren von Optionen in Nicht-Optionen entwickelt wurde. Es hat die Syntax a ?? b
, wobei a
es sich um einen optionalen Typ handelt und b
derselbe Typ ist wiea
(obwohl normalerweise nicht optional).
Sie können im Wesentlichen sagen: „Wenn a
ein Wert enthalten ist, packen Sie ihn aus. Wenn dies nicht der Fall ist, kehren Sie b
stattdessen zurück. “ Zum Beispiel könnten Sie es so verwenden:
let number = anOptionalInt ?? 0
Dadurch wird eine number
Konstante vom Int
Typ definiert, die entweder den Wert von anOptionalInt
enthält, wenn sie einen Wert enthält, oder auf 0
andere Weise.
Es ist nur eine Abkürzung für:
let number = anOptionalInt != nil ? anOptionalInt! : 0
Optionale Verkettung
Sie können die optionale Verkettung verwenden, um eine Methode aufzurufen oder auf eine optionale Eigenschaft zuzugreifen. Dies erfolgt einfach durch Suffixieren des Variablennamens mit a?
bei Verwendung .
Angenommen, wir haben eine Variable foo
vom Typ einer optionalen Foo
Instanz.
var foo : Foo?
Wenn wir eine Methode aufrufen wollten foo
, die nichts zurückgibt, können wir einfach Folgendes tun:
foo?.doSomethingInteresting()
Wenn foo
ein Wert enthalten ist, wird diese Methode darauf aufgerufen. Wenn dies nicht der Fall ist, passiert nichts Schlimmes - der Code wird einfach weiter ausgeführt.
(Dies ähnelt dem Senden von Nachrichten an nil
Objective-C.)
Dies kann daher auch zum Festlegen von Eigenschaften sowie von Aufrufmethoden verwendet werden. Zum Beispiel:
foo?.bar = Bar()
Auch hier wird nichts Schlimmes passieren, wenn es so foo
ist nil
. Ihr Code wird einfach weiter ausgeführt.
Ein weiterer nützlicher Trick, den Sie mit der optionalen Verkettung ausführen können, besteht darin, zu überprüfen, ob das Festlegen einer Eigenschaft oder das Aufrufen einer Methode erfolgreich war. Sie können dies tun, indem Sie den Rückgabewert mit vergleichen nil
.
(Dies liegt daran, dass ein optionaler Wert zurückgegeben wird Void?
und nicht Void
für eine Methode, die nichts zurückgibt.)
Zum Beispiel:
if (foo?.bar = Bar()) != nil {
print("bar was set successfully")
} else {
print("bar wasn’t set successfully")
}
Beim Versuch, auf Eigenschaften zuzugreifen oder Methoden aufzurufen, die einen Wert zurückgeben, wird es jedoch etwas schwieriger. Da dies foo
optional ist, ist alles, was von ihm zurückgegeben wird, ebenfalls optional. Um dies zu beheben, können Sie entweder die zurückgegebenen Optionen mit einer der oben genannten Methoden auspacken oder sich foo
selbst auspacken , bevor Sie auf Methoden zugreifen oder Methoden aufrufen, die Werte zurückgeben.
Wie der Name schon sagt, können Sie diese Anweisungen auch miteinander verketten. Dies bedeutet, dass Sie, wenn Sie foo
eine optionale Eigenschaft haben baz
, die eine Eigenschaft qux
hat, Folgendes schreiben können:
let optionalQux = foo?.baz?.qux
Da foo
und baz
optional sind, ist der von zurückgegebene Wert qux
immer optional, unabhängig davon, ob er qux
selbst optional ist.
map
und flatMap
Eine häufig nicht ausreichend genutzte Funktion mit Optionen ist die Möglichkeit, die Funktionen map
und zu flatMap
verwenden. Mit diesen können Sie nicht optionale Transformationen auf optionale Variablen anwenden. Wenn ein optionales Element einen Wert hat, können Sie eine bestimmte Transformation darauf anwenden. Wenn es keinen Wert hat, bleibt es nil
.
Angenommen, Sie haben eine optionale Zeichenfolge:
let anOptionalString:String?
Durch Anwenden der map
Funktion darauf können wir die stringByAppendingString
Funktion verwenden, um sie mit einer anderen Zeichenfolge zu verketten.
Da stringByAppendingString
ein nicht optionales Zeichenfolgenargument verwendet wird, können wir unsere optionale Zeichenfolge nicht direkt eingeben. Mit using map
können wir jedoch allow stringByAppendingString
verwenden, wenn anOptionalString
ein Wert vorhanden ist.
Zum Beispiel:
var anOptionalString:String? = "bar"
anOptionalString = anOptionalString.map {unwrappedString in
return "foo".stringByAppendingString(unwrappedString)
}
print(anOptionalString) // Optional("foobar")
Wenn anOptionalString
jedoch kein Wert vorhanden ist, map
wird zurückgegeben nil
. Zum Beispiel:
var anOptionalString:String?
anOptionalString = anOptionalString.map {unwrappedString in
return "foo".stringByAppendingString(unwrappedString)
}
print(anOptionalString) // nil
flatMap
funktioniert ähnlich wie map
, außer dass Sie eine weitere Option innerhalb des Verschlusskörpers zurückgeben können. Dies bedeutet, dass Sie eine Option in einen Prozess eingeben können, für den eine nicht optionale Eingabe erforderlich ist, eine Option jedoch selbst ausgeben kann.
try!
Das Fehlerbehandlungssystem von Swift kann sicher mit Do-Try-Catch verwendet werden :
do {
let result = try someThrowingFunc()
} catch {
print(error)
}
Wenn someThrowingFunc()
ein Fehler ausgelöst wird, wird der Fehler sicher im catch
Block abgefangen .
Die error
Konstante, die Sie im catch
Block sehen, wurde von uns nicht deklariert - sie wird automatisch von generiert catch
.
Sie können sich auch error
selbst deklarieren , es hat den Vorteil, dass Sie es in ein nützliches Format umwandeln können, zum Beispiel:
do {
let result = try someThrowingFunc()
} catch let error as NSError {
print(error.debugDescription)
}
Auf try
diese Weise können Sie Fehler von Wurffunktionen versuchen, abfangen und behandeln.
Es gibt auch, try?
was den Fehler absorbiert:
if let result = try? someThrowingFunc() {
// cool
} else {
// handle the failure, but there's no error information available
}
Das Fehlerbehandlungssystem von Swift bietet jedoch auch eine Möglichkeit, den Versuch zu erzwingen try!
:
let result = try! someThrowingFunc()
Die in diesem Beitrag erläuterten Konzepte gelten auch hier: Wenn ein Fehler ausgegeben wird, stürzt die Anwendung ab.
Sie sollten es nur verwenden, try!
wenn Sie nachweisen können, dass das Ergebnis in Ihrem Kontext niemals fehlschlägt - und dies ist sehr selten.
Meistens verwenden Sie das komplette Do-Try-Catch-System - und das optionale try?
- in den seltenen Fällen, in denen die Behandlung des Fehlers nicht wichtig ist.
Ressourcen