Optionals im Detail

Optionals sind bekanntermaßen eines der spannendsten und mächtigsten Sprachmerkmale in Swift. Interessant ist hierbei, wie Optionals intern in Swift abgebildet werden. Denn genau genommen ist die typische Optional-Syntax, wie sie ständig in Swift zum Einsatz kommt, lediglich eine Art Kurzschreibweise. Im Hintergrund kommt ein Typ zum Einsatz, den man normalerweise nie explizit verwendet, aber für die korrekte Funktionsweise von Optionals unabdingbar ist.

Optional: eine Generic Enumeration

Innerhalb der Swift Standard Library ist ein Typ namens Optional definiert. Dabei handelt es sich um eine Generic Enerumation, also eine Enumeration, die auf einem dynamischen Associated Value basiert. Dieses Prinzip kennt man in vergleichbarer Form zum Beispiel von Arrays. Arrays in Swift sind nicht einfach nur Structures, sondern Generic Structures. Das ermöglicht es, dass man sowohl Arrays auf Basis von beispielsweise Int– als auch solche auf Basis von String-Instanzen erstellen kann.

Bei "Optional" handelt es sich um eine Generic Enumeration aus der Swift Standard Library.
Bei „Optional“ handelt es sich um eine Generic Enumeration aus der Swift Standard Library.

Die Generic Enumeration Optional ist die eigentliche Logik hinter der Arbeit mit Optionals. Das folgende Listing demonstriert, wie sich Optionals alternativ auf Basis dieser Enumeration erzeugen lassen (im Vergleich zur „herkömmlichen“ Erstellung eines Optionals):

var someInt1: Int? = 19
var someInt2: Optional<Int> = 19

Hinter der Kurzschreibweise Int? verbirgt sich also der Befehl Optional<Int>. Analog dazu verhält es sich wie bei den beschriebenen Arrays: [Int] hat in Swift dieselbe Bedeutung wie Array<Int>.

Alle Optionals in Swift sind somit Instanzen des Typs Optional. Durch die Generics-Zuweisung wird pro Instanz definiert, welche Art von Instanzen ein Optional aufnehmen kann (im gezeigten Fall handelt es sich um Integer).

Wert oder kein Wert?

Doch es geht noch weiter. Die Optional-Enumeration definiert zwei Werte, die Instanzen annehmen können: none und some(_:). Ersterer bedeutet, dass das entsprechende Optional keinen Wert besitzt und somit nil entspricht. Letzterer weist dem Optional einen Wert mittels Associated Value zu.

Zum besseren Verständnis sollen diese Enumeration-Werte ebenfalls anhand eines kleinen Beispiels dem „typischen“ Umgang mit Optionals gegenüber gestellt werden. Zunächst erfolgt erneut die Erzeugung eines Optionals inklusive Wertzuweisung, im nächsten Schritt wird jener Wert wieder vom Optional entfernt.

var someInt1: Int? = 19
someInt1 = nil

var someInt2: Optional<Int> = .some(19)
someInt2 = .none

Das Vorgehen für someInt1 und someInt2 ist in beiden Fällen absolut identisch. Beim Einsatz des Cases some(_:) ermittelt Swift den Typ des zugewiesenen Associated Values. Der muss mit der Deklaration (in diesem Fall Optional<Int>) übereinstimmen, ansonsten kommt es zu einem Fehler. Das ist nur logisch, schließlich kann ein Optional nur Instanzen eines ganz konkreten Typs entgegennehmen. So können wir someInt1 und someInt2 in diesem Beispiel jeden beliebigen Integer, aber keinen String (oder irgendeinen anderen Typ) zuweisen.

Zugriff auf den Wert

Um den (potentiellen) Wert eines Optionals auszulesen, nutzen wir typischerweise die Unwrap-Syntax mittels Ausrufezeichen (!). Auch hierfür bringt die Optional-Enumeration intern eine passende Funktionalität mit, nämlich in Form der unsafelyUnwrapped-Property. Die auf einem Optional aufzurufen hat denselben Effekt wie das Setzen des Ausrufezeichens. Man sollte also vorher unbedingt sicherstellen, dass das entsprechende Optional auch wirklich einen Wert besitzt (zum Beispiel durch Prüfung gegen nil).

Das folgende Listing stellt zum Abschluss auch diese Technik dem bekannten Verfahren gegenüber:

if someInt1 != nil {
    print(someInt1!)
}

if someInt2 != nil {
    print(someInt2.unsafelyUnwrapped)
}

Fazit

Ich muss an dieser Stelle sagen, dass ich die in diesem Artikel beschriebene Syntax für Optionals auf Basis der Optional-Enumeration noch nie in der Praxis eingesetzt habe (und sehr wahrscheinlich auch nie einsetzen werde). Allerdings zeigt sie sehr schön, wie Optionals intern eigentlich funktionieren und was bei der Arbeit mit ihnen geschieht. Weißen wir beispielsweise einem Optional einen Wert zu, entspricht das der Zuweisung des Cases some(_:). Greifen wir auf den Wert eines Optionals mittels ! zu, führt das zum Aufruf der unsafelyUnwrapped-Property.

Im Umkehrschluss bedeutet das auch, dass wir ausschließlich mit Kurzschreibweisen in Swift arbeiten, die in die passenden beschriebenen Funktionen übersetzt werden. Ich persönlich finde das enorm spannend, und wer mehr wissen möchte, sollte unbedingt einmal einen näheren Blick auf die Dokumentation zur Optional-Enumeration werfen.

Euer Thomas


Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert