Ein optionales Element in Swift ist ein Typ, der entweder einen Wert oder keinen Wert enthalten kann. Optionale Optionen werden durch Anhängen von a ?
an einen beliebigen Typ geschrieben:
var name: String? = "Bertie"
Optionals (zusammen mit Generics) sind eines der am schwierigsten zu verstehenden Swift-Konzepte. Aufgrund der Art und Weise, wie sie geschrieben und verwendet werden, ist es leicht, eine falsche Vorstellung davon zu bekommen, was sie sind. Vergleichen Sie die obige Option mit der Erstellung einer normalen Zeichenfolge:
var name: String = "Bertie" // No "?" after String
Aus der Syntax geht hervor, dass ein optionaler String einem normalen String sehr ähnlich ist. Es ist nicht. Ein optionaler String ist kein String mit aktivierter "optionaler" Einstellung. Es ist keine spezielle Sorte von String. Ein String und ein optionaler String sind völlig unterschiedliche Typen.
Hier ist das Wichtigste zu wissen: Ein optionales ist eine Art Container. Ein optionaler String ist ein Container, der möglicherweise einen String enthält. Ein optionales Int ist ein Container, der ein Int enthalten kann. Stellen Sie sich ein optionales Paket als eine Art Paket vor. Bevor Sie es öffnen (oder in der Sprache der Optionen "auspacken"), wissen Sie nicht, ob es etwas oder nichts enthält.
Sie können sehen, wie Optionen in der Swift-Standardbibliothek implementiert sind, indem Sie "Optional" in eine Swift-Datei eingeben und ⌘ darauf klicken. Hier ist der wichtige Teil der Definition:
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
Optional ist nur enum
einer von zwei Fällen: .none
oder .some
. Wenn ja .some
, gibt es einen zugeordneten Wert, der im obigen Beispiel String
"Hallo" wäre. Ein optionales Element verwendet Generics, um dem zugeordneten Wert einen Typ zuzuweisen. Der Typ eines optionalen String ist nicht String
, es ist Optional
, oder genauer gesagt Optional<String>
.
Alles, was Swift mit Optionen macht, ist magisch, um das Lesen und Schreiben von Code flüssiger zu gestalten. Leider verdeckt dies die Art und Weise, wie es tatsächlich funktioniert. Ich werde später einige der Tricks durchgehen.
Hinweis: Ich werde viel über optionale Variablen sprechen, aber es ist auch in Ordnung, optionale Konstanten zu erstellen. Ich markiere alle Variablen mit ihrem Typ, um das Verständnis der erstellten Typtypen zu erleichtern, aber Sie müssen dies nicht in Ihrem eigenen Code tun.
So erstellen Sie Optionen
Um ein optionales Element zu erstellen, fügen Sie ?
nach dem Typ, den Sie umbrechen möchten, ein an. Jeder Typ kann optional sein, auch Ihre eigenen benutzerdefinierten Typen. Sie können kein Leerzeichen zwischen dem Typ und dem haben ?
.
var name: String? = "Bob" // Create an optional String that contains "Bob"
var peter: Person? = Person() // An optional "Person" (custom type)
// A class with a String and an optional String property
class Car {
var modelName: String // must exist
var internalName: String? // may or may not exist
}
Optionale Optionen verwenden
Sie können ein optionales Element vergleichen, nil
um festzustellen, ob es einen Wert hat:
var name: String? = "Bob"
name = nil // Set name to nil, the absence of a value
if name != nil {
print("There is a name")
}
if name == nil { // Could also use an "else"
print("Name has no value")
}
Das ist etwas verwirrend. Dies impliziert, dass ein optionales Element entweder das eine oder das andere ist. Es ist entweder Null oder es ist "Bob". Dies ist nicht wahr, das Optionale verwandelt sich nicht in etwas anderes. Der Vergleich mit Null ist ein Trick, um den Code besser lesbar zu machen. Wenn eine Option gleich Null ist, bedeutet dies nur, dass die Aufzählung derzeit auf gesetzt ist .none
.
Nur Optionen können Null sein
Wenn Sie versuchen, eine nicht optionale Variable auf Null zu setzen, wird eine Fehlermeldung angezeigt.
var red: String = "Red"
red = nil // error: nil cannot be assigned to type 'String'
Eine andere Sichtweise auf Optionen ist die Ergänzung zu normalen Swift-Variablen. Sie sind ein Gegenstück zu einer Variablen, deren Wert garantiert ist. Swift ist eine vorsichtige Sprache, die Mehrdeutigkeiten hasst. Die meisten Variablen werden als nicht optional definiert, aber manchmal ist dies nicht möglich. Stellen Sie sich beispielsweise einen View Controller vor, der ein Bild entweder aus einem Cache oder aus dem Netzwerk lädt. Möglicherweise ist dieses Bild zum Zeitpunkt der Erstellung des Ansichtscontrollers vorhanden oder nicht. Es gibt keine Möglichkeit, den Wert für die Bildvariable zu garantieren. In diesem Fall müssten Sie es optional machen. Es beginnt, nil
wenn das Bild abgerufen wird und das optionale einen Wert erhält.
Die Verwendung einer Option zeigt die Absicht des Programmierers. Im Vergleich zu Objective-C, bei dem jedes Objekt Null sein könnte, muss Swift klar sein, wann ein Wert fehlen kann und wann er garantiert existiert.
Um ein optionales zu verwenden, "packen" Sie es aus
Ein optionales String
kann nicht anstelle eines tatsächlichen verwendet werden String
. Um den umschlossenen Wert in einem optionalen Wert zu verwenden, müssen Sie ihn auspacken. Der einfachste Weg, ein optionales Element zu entpacken, besteht darin, !
nach dem optionalen Namen ein nachträgliches hinzuzufügen . Dies wird als "Force Unwrapping" bezeichnet. Es gibt den Wert innerhalb des optionalen (als ursprünglichen Typ) zurück, aber wenn das optionale ist nil
, verursacht es einen Laufzeitabsturz. Vor dem Auspacken sollten Sie sicher sein, dass es einen Wert gibt.
var name: String? = "Bob"
let unwrappedName: String = name!
print("Unwrapped name: \(unwrappedName)")
name = nil
let nilName: String = name! // Runtime crash. Unexpected nil.
Überprüfen und Verwenden eines optionalen
Da Sie vor dem Auspacken und Verwenden eines optionalen Elements immer auf Null prüfen sollten, ist dies ein häufiges Muster:
var mealPreference: String? = "Vegetarian"
if mealPreference != nil {
let unwrappedMealPreference: String = mealPreference!
print("Meal: \(unwrappedMealPreference)") // or do something useful
}
In diesem Muster überprüfen Sie, ob ein Wert vorhanden ist. Wenn Sie sicher sind, dass dies der Fall ist, erzwingen Sie das Entpacken in eine temporäre Konstante. Da dies so häufig vorkommt, bietet Swift eine Verknüpfung mit "if let" an. Dies wird als "optionale Bindung" bezeichnet.
var mealPreference: String? = "Vegetarian"
if let unwrappedMealPreference: String = mealPreference {
print("Meal: \(unwrappedMealPreference)")
}
Dies erzeugt eine temporäre Konstante (oder eine Variable , wenn Sie ersetzen let
mit var
) , dessen Umfang ist nur innerhalb der , wenn die Klammern. Da die Verwendung eines Namens wie "unwrappedMealPreference" oder "realMealPreference" eine Belastung darstellt, können Sie mit Swift den ursprünglichen Variablennamen wiederverwenden und einen temporären Namen innerhalb des Klammerbereichs erstellen
var mealPreference: String? = "Vegetarian"
if let mealPreference: String = mealPreference {
print("Meal: \(mealPreference)") // separate from the other mealPreference
}
Hier ist ein Code, der zeigt, dass eine andere Variable verwendet wird:
var mealPreference: String? = "Vegetarian"
if var mealPreference: String = mealPreference {
print("Meal: \(mealPreference)") // mealPreference is a String, not a String?
mealPreference = "Beef" // No effect on original
}
// This is the original mealPreference
print("Meal: \(mealPreference)") // Prints "Meal: Optional("Vegetarian")"
Die optionale Bindung überprüft, ob die Option gleich Null ist. Wenn dies nicht der Fall ist, wird das optionale Element in die angegebene Konstante entpackt und der Block ausgeführt. In Xcode 8.3 und höher (Swift 3.1) führt der Versuch, eine solche Option zu drucken, zu einer nutzlosen Warnung. Verwenden Sie die optionalen Optionen debugDescription
, um es zum Schweigen zu bringen:
print("\(mealPreference.debugDescription)")
Wofür sind Optionen?
Optionale haben zwei Anwendungsfälle:
- Dinge, die scheitern können (ich hatte etwas erwartet, aber nichts bekommen)
- Dinge, die jetzt nichts sind, aber etwas später sein könnten (und umgekehrt)
Einige konkrete Beispiele:
- Eine Eigenschaft, die dort sein kann oder nicht, wie
middleName
oder spouse
in einer Person
Klasse
- Eine Methode, die einen Wert oder nichts zurückgeben kann, z. B. die Suche nach einer Übereinstimmung in einem Array
- Eine Methode, die entweder ein Ergebnis zurückgeben oder einen Fehler erhalten und nichts zurückgeben kann, z. B. den Versuch, den Inhalt einer Datei zu lesen (die normalerweise die Daten der Datei zurückgibt), aber die Datei existiert nicht
- Delegieren Sie Eigenschaften, die nicht immer festgelegt werden müssen und in der Regel nach der Initialisierung festgelegt werden
- Für
weak
Eigenschaften in Klassen. Das, worauf sie zeigen, kann jederzeit eingestellt nil
werden
- Eine große Ressource, die möglicherweise freigegeben werden muss, um Speicher zurückzugewinnen
- Wenn Sie wissen möchten, wann ein Wert festgelegt wurde (Daten noch nicht geladen> die Daten), anstatt ein separates dataLoaded zu verwenden
Boolean
In Objective-C gibt es keine Optionen, aber es gibt ein gleichwertiges Konzept, das null zurückgibt. Methoden, die ein Objekt zurückgeben können, können stattdessen nil zurückgeben. Dies bedeutet "das Fehlen eines gültigen Objekts" und wird oft verwendet, um zu sagen, dass etwas schief gelaufen ist. Es funktioniert nur mit Objective-C-Objekten, nicht mit Grundelementen oder grundlegenden C-Typen (Aufzählungen, Strukturen). Objective-C hatte häufig spezielle Typen, um das Fehlen dieser Werte darzustellen ( NSNotFound
was wirklich bedeutet NSIntegerMax
, kCLLocationCoordinate2DInvalid
eine ungültige Koordinate darzustellen, -1
oder es werden auch einige negative Werte verwendet). Der Codierer muss diese speziellen Werte kennen, damit sie für jeden Fall dokumentiert und gelernt werden müssen. Wenn eine Methode nicht nil
als Parameter verwendet werden kann, muss dies dokumentiert werden. In Objective-Cnil
war ein Zeiger, genau wie alle Objekte als Zeiger definiert wurden, aber nil
auf eine bestimmte (Null-) Adresse zeigten. In Swift nil
ist ein Literal, was das Fehlen eines bestimmten Typs bedeutet.
Verglichen mit nil
Früher konnten Sie alle optionalen Optionen verwenden als Boolean
:
let leatherTrim: CarExtras? = nil
if leatherTrim {
price = price + 1000
}
In neueren Versionen von Swift müssen Sie verwenden leatherTrim != nil
. Warum ist das? Das Problem ist, dass a Boolean
in ein optionales eingewickelt werden kann. Wenn Ihnen das Boolean
gefällt:
var ambiguous: Boolean? = false
Es gibt zwei Arten von "falsch", eine, bei der es keinen Wert gibt, und eine, bei der es einen Wert hat, aber der Wert ist false
. Swift hasst Mehrdeutigkeiten, daher müssen Sie jetzt immer eine Option gegen prüfen nil
.
Sie fragen sich vielleicht, wozu ein optionales Element gut Boolean
ist? Wie bei anderen Optionen könnte der .none
Status anzeigen, dass der Wert noch unbekannt ist. Möglicherweise befindet sich am anderen Ende eines Netzwerkanrufs etwas, dessen Abfrage einige Zeit in Anspruch nimmt. Optionale Boolesche Werte werden auch als " Drei-Wert-Boolesche Werte " bezeichnet.
Schnelle Tricks
Swift verwendet einige Tricks, damit Optionen funktionieren. Betrachten Sie diese drei Zeilen mit normal aussehendem optionalem Code.
var religiousAffiliation: String? = "Rastafarian"
religiousAffiliation = nil
if religiousAffiliation != nil { ... }
Keine dieser Zeilen sollte kompiliert werden.
- In der ersten Zeile wird ein optionaler String mit einem String-Literal festgelegt, zwei verschiedenen Typen. Auch wenn dies ein war, sind
String
die Typen unterschiedlich
- In der zweiten Zeile wird ein optionaler String auf null gesetzt, zwei verschiedene Typen
- Die dritte Zeile vergleicht eine optionale Zeichenfolge mit null, zwei verschiedenen Typen
Ich werde einige Implementierungsdetails von Optionen durchgehen, mit denen diese Zeilen funktionieren.
Optional erstellen
Mit ?
einem optionalen erstellen ist syntaktischer Zucker, durch den Compiler Swift aktiviert. Wenn Sie es auf lange Sicht tun möchten, können Sie eine Option wie die folgende erstellen:
var name: Optional<String> = Optional("Bob")
Dies ruft Optional
den ersten Initialisierer auf, public init(_ some: Wrapped)
der den zugehörigen Typ des optionalen Typs von dem in den Klammern verwendeten Typ ableitet.
Die noch längere Möglichkeit, eine Option zu erstellen und festzulegen:
var serialNumber:String? = Optional.none
serialNumber = Optional.some("1234")
print("\(serialNumber.debugDescription)")
Optionale Einstellung auf nil
Sie können eine Option ohne Anfangswert oder eine Option mit dem Anfangswert von erstellen nil
(beide haben das gleiche Ergebnis).
var name: String?
var name: String? = nil
Das Zulassen, dass Optionen gleich sind, nil
wird durch das Protokoll ExpressibleByNilLiteral
(zuvor benannt NilLiteralConvertible
) aktiviert . Das optionale wird mit Optional
dem zweiten Initialisierer erstellt public init(nilLiteral: ())
. Die Dokumente sagen, dass Sie ExpressibleByNilLiteral
nichts anderes als Optionen verwenden sollten, da dies die Bedeutung von Null in Ihrem Code ändern würde, aber es ist möglich, dies zu tun:
class Clint: ExpressibleByNilLiteral {
var name: String?
required init(nilLiteral: ()) {
name = "The Man with No Name"
}
}
let clint: Clint = nil // Would normally give an error
print("\(clint.name)")
Mit demselben Protokoll können Sie eine bereits erstellte Option auf festlegen nil
. Obwohl dies nicht empfohlen wird, können Sie den Null-Literal-Initialisierer direkt verwenden:
var name: Optional<String> = Optional(nilLiteral: ())
Vergleich eines optionalen mit nil
Optionals definieren zwei spezielle "==" - und "! =" - Operatoren, die Sie in der Optional
Definition sehen können. Mit der ersten ==
Option können Sie überprüfen, ob eine Option gleich Null ist. Zwei verschiedene Optionen, die auf .none gesetzt sind, sind immer gleich, wenn die zugehörigen Typen gleich sind. Wenn Sie mit nil vergleichen, erstellt Swift hinter den Kulissen eine Option desselben zugeordneten Typs, die auf .none gesetzt ist, und verwendet diese dann für den Vergleich.
// How Swift actually compares to nil
var tuxedoRequired: String? = nil
let temp: Optional<String> = Optional.none
if tuxedoRequired == temp { // equivalent to if tuxedoRequired == nil
print("tuxedoRequired is nil")
}
Mit dem zweiten ==
Operator können Sie zwei Optionen vergleichen. Beide müssen vom selben Typ sein und dieser Typ muss übereinstimmen Equatable
(das Protokoll, mit dem Dinge mit dem regulären Operator "==" verglichen werden können). Swift packt (vermutlich) die beiden Werte aus und vergleicht sie direkt. Es behandelt auch den Fall, in dem eine oder beide Optionen verfügbar sind .none
. Beachten Sie den Unterschied zwischen dem Vergleich mit dem nil
Literal.
Darüber hinaus können Sie jeden Equatable
Typ mit einem optionalen Wrapping dieses Typs vergleichen:
let numberToFind: Int = 23
let numberFromString: Int? = Int("23") // Optional(23)
if numberToFind == numberFromString {
print("It's a match!") // Prints "It's a match!"
}
Hinter den Kulissen verpackt Swift das Nicht-Optionale vor dem Vergleich als Optional. Es funktioniert auch mit Literalen ( if 23 == numberFromString {
)
Ich sagte, es gibt zwei ==
Operatoren, aber es gibt tatsächlich einen dritten, mit dem Sie nil
die linke Seite des Vergleichs angeben können
if nil == name { ... }
Optionale benennen
Es gibt keine Swift-Konvention, um optionale Typen anders als nicht optionale Typen zu benennen. Die Leute vermeiden es, dem Namen etwas hinzuzufügen, um anzuzeigen, dass es sich um einen optionalen Typ handelt (z. B. "optionalMiddleName" oder "möglicherNumberAsString"), und lassen die Deklaration anzeigen, dass es sich um einen optionalen Typ handelt. Dies wird schwierig, wenn Sie etwas benennen möchten, das den Wert von einem optionalen Wert enthält. Der Name "middleName" impliziert, dass es sich um einen String-Typ handelt. Wenn Sie also den String-Wert daraus extrahieren, erhalten Sie häufig Namen wie "actualMiddleName" oder "unwrappedMiddleName" oder "realMiddleName". Verwenden Sie die optionale Bindung und verwenden Sie den Variablennamen erneut, um dies zu umgehen.
Die offizielle Definition
Aus "The Basics" in der Swift-Programmiersprache :
Swift führt auch optionale Typen ein, die das Fehlen eines Werts behandeln. Optionale Optionen sagen entweder "es gibt einen Wert und er ist gleich x" oder "es gibt überhaupt keinen Wert". Optionals ähneln der Verwendung von nil mit Zeigern in Objective-C, funktionieren jedoch für jeden Typ, nicht nur für Klassen. Optionals sind sicherer und aussagekräftiger als Nullzeiger in Objective-C und bilden das Herzstück vieler der leistungsstärksten Funktionen von Swift.
Optionals sind ein Beispiel für die Tatsache, dass Swift eine typsichere Sprache ist. Swift 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 ein Int übergeben. Auf diese Weise können Sie Fehler so früh wie möglich im Entwicklungsprozess erkennen und beheben.
Zum Abschluss hier ein Gedicht aus dem Jahr 1899 über Optionen:
Gestern auf der Treppe
traf ich einen Mann, der nicht da war.
Er war heute nicht wieder da.
Ich wünschte, er würde Antigonish weggehen
Mehr Ressourcen: