Optionals
Optionals sind ein grundlegendes Konzept in Swift. Die Entwickler von Swift sagen selbst das viele neue Möglichkeiten der Sprache ohne diesen Mechanismus nicht existieren könnten. Aber was sind Optionals? In dieser Lektion werden wir in einigen Beispielen unterschiedliche Szenarien und Möglichkeiten darstellen.
Optionals sind gerade für Anfänger kein leichtes Thema, dass in 2 Minuten erklärt sein wird. Nehmen Sie sich die Zeit um die Syntax und ihre Anwendungsmöglichkeiten zu verstehen.
Begriffserklärung
Optionals bieten uns die Möglichkeit Variablen oder Konstanten zu deklarieren ohne sie mit einem Wert zu initialisieren. Das bedeutet für uns das der Inhalt einer Variable vorhanden sein kann, aber nicht muss. „Nicht muss“ ist hier entscheidend, da wir auf genau diesen Zustand prüfen. Sollte sich herausstellen das der Wert nicht existiert können wir entsprechend reagieren.
Mit Optionals können wir einem Programmcode flexibler gestalten. Ausserdem kann Swift durch dessen Typensicherheit mehr Fehlerschutz im Umgang mit Variablen und Konstanten bieten als Objective-C.
Für diesen Teil gibt es auch ein Video Tutorial hier auf YouTube.
Optional
Damit wir einen optionalen Wert deklarieren können müssen wir der Variable den Variablentyp immer mit angeben. Das haben wir schon mehrfach getan, jetzt kommt aber die Besonderheit hinzu das wir ein ? hinter den Typen schreiben.
1 |
var meineZahl: Int? |
In unserem Playground werden wir jetzt auf der rechten Seite keinen Zahlenwert sehen sonder nil. nil entspricht einem nicht-existenten Wert. Man kann ihn zum Beispiel vergleichen mit NULL aus der Programmiersprache C oder PHP. Der Inhalt der Variable ist also nicht-existent.
Was bringt mir dieser Zustand?
Es könnte z.B. sein das Ihr Benutzer auf Ihrer Programmoberfläche einen Wert eingeben soll. Wenn dieser Wert aber nicht existiert können Sie ihn auch nicht verwenden. Um auf Nummer sicher zu gehen, dass der Wert nicht existieren kann wird das im Textfield im Normfall als optional markiert.
Später werden wir das Verhalten auch sehen, wenn wir die erste iOS App mit UI Elementen schreiben . Diese sind bis zu ihrer Initialisierung nicht verfügbar, es könnte aber passieren das unser Code bereits auf diese Elemente zugreifen möchte. Dankbare Weise stürzt das Programm aber nicht ab da es bereits weiss, dass die Variable keinen Wert haben muss.
1 2 3 |
var meineZahl: Int? // Initialisierung ist nil meineZahl = 12 // Setzt Wert auf 12 meineZahl = nil // Setzt wieder auf nil |
Wenn wir die Ausgabe von REPL im Playground ansehen werden wir feststellen, dass bei der Deklaration der Wert nil ist. Sobald wir den Wert setzen wird REPL uns ausgeben, dass der Inhalt {Some 12} ist. Im Anschluss setzen wir den Wert wieder auf nil , wodurch die Variable wieder leer ist.
LogicValue und unwrapped optionals
Warum gibt uns REPL bei unserem letzten Beispiel {Some 12} aus (Ausgabe wie immer im Playground rechts sichtbar)? Dafür müssen wir ein wenig hinter die Kulissen von Swift kucken.
Optionals gibt es in zwei Ausprägungen. „Normal optional“ und „Implicit unwrapped optionals“. Ein Optional, den wir durch das Fragezeichen markieren, wird nicht direkt als Wert der Variable gespeichert sondern in einem Datentyp (vereinfacht gesagt!), welcher den Zugriff auf diesen Wert regelt und kontrolliert. Dieser Datentyp gibt beim Zugriff immer einen Bool-Wert zurück. Dies geht aber nur mit Datentypen die das Protokoll LogicValue implementieren. Zu Protokollen kommen wir aber in einer anderen Lektion.
Um an den Wert eines optionals zu kommen muss man ihn erst einmal „auspacken„. Dies erreichen wir durch das „!“ Zeichen.
1 2 |
var meinString: String? = "Hallo" // {Some "Hallo"} var neuerString: String = meinString! // Hallo |
Im Playground sehen wir rechts wieder das meinString den Wert {Some "Hallo"} hat. Möchten wir den Wert der Variable meinString in einer anderen speichern die nicht optional ist, müssen wir diese zuerst „unwrappen“ oder anderes ausgedrückt die Daten „auspacken“. Dies machen wir indem wir ein „!“ hinter den Variablennamen schreiben, wo dieser entpackt werden soll.
Unsere zweite Variable neuerString ist deklariert als String und erwartet einen solchen auch. Wir können nicht einfach den Wert einer andere Variable, die als optional deklariert ist, speichern. Sollten wir das „!“ Zeichen weglassen würde uns der Compiler das direkt als Fehler anzeigen.
Das kann uns natürlich später auch in unserem eigenen Programm passieren, wie in der Einleitung bereits erwähnt durch eine fehlende Benutzereingabe. Wie können wir es vermeiden, dass ein Wert entpackt wird der gar nicht existiert?
Swift bietet uns hierfür zwei Möglichkeiten. Da ein optional im Grunde ein LogicValue ist können wir einfach mit einer Kontrollstruktur prüfen ob der Inhalt von meinString existiert. Sollte dies der Fall sein, weisen wir neuerString den Wert zu nachdem wir ihn mit dem „!“-Zeichen entpackt haben. Eine weitere Möglichkeit ist es eine temporäre Konstante zu erstellen und das direkt bei der Definition unserer Kontrollstruktur. Durch die Zuweisung eines optionals Wertes wird der Wert – von Swift selbst – beim vorhanden sein entpackt und hier in der Konstante x gespeichert, sofern dieser nicht nil ist.
Im Anschluss können wir den Wert aus der Konstanten x der Variable neuerString zuweisen.
Sicherlich verwirrt dies den ein oder anderen ein wenig, aber mit etwas Übung und späteren arbeiten in Verbindung mit Apple’s Systembibliotheken (UIKit oder Cocoa) wird das ganze ein Kinderspiel.
Neben der Möglichkeit eine Variable als optional deklarieren, können wir auch auch sagen das diese „implicit unwrapped optional“ ist. Das heißt, dass wir garantieren das hier bereits der ausgepackte Wert hinter dem Variablennamen verborgen ist oder dieser nil ist. Wir müssen ihn also nicht explizit entpacken. Wir bekommen auch keinen Fehler wenn wir versuchen meinString , ohne das wir diese vorher in Kontrollstruktur geprüft haben, direkt der Variable neuerString zuweisen. Das funktioniert aber natürlich nur solange meinString nicht den Wert nil hat.
1 2 3 4 5 6 7 8 9 10 11 |
var neuerString: String // Deklaration ohne Initialisierung var meinString: String! = "Hallo" // Hallo meinString = nil // Variablen nil'len // Prüfen und zuweisen if meinString { neuerString = meinString } // Laufzeitfehler wenn meinString == nil neuerString = meinString |
Als Entwickler haben wir die Möglichkeit anderen Entwicklern, die vielleicht Teile unseres Programmecodes wiederverwenden, darauf hinzuweisen das einige Werte nicht vorhanden sein müssen. Es obliegt dann den anderen Entwicklern sicherzustellen, dass sie immer mit Werten arbeitet die auch Tatsächlich existieren. Sollten sie dies nicht tun, laufen sie Gefahr durch dieses Fehlverhalten ihre App mit Laufzeitfehlern abstürzten zu lassen. Sehr zum Ärgern des Endanwenders.
Optionals und Funktionen
Selbstverständlich können wir optionals auch bei Funktionen nutzen. Diese Technik kann sowohl bei den Parametern, als auch bei den Rückgabewerten genutzt werden. Man könnte z.B. einen Fehlerfall definieren indem eine Funktion anstelle von einem Bool einfach nil zurückliefert. Dies kann Beispielsweise dann nützlich sein, wenn man eine Funktion hat die einen String zurückliefern soll.
Hierdurch ersparen wir uns nämlich ein Tuple, die uns sowohl einen Bool als auch irgendeinen String zurückliefern müsste. Schauen wir uns das folgende Beispiel mit optionals und ohne an.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
/** Ohne optionals */ func stringFunktion(xString: String) -> (Erfolg: Bool, Wert: String) { // Wir geben direkt einen Fehler zurück, egal was geliefert wird return (false, "") } var (ergebnis, meinString) = stringFunktion("XYZ") if ergebnis { println("String vorhanden, alles gut!") } /* ------------------------------------------ */ /* ------------------------------------------ */ /* ------------------------------------------ */ /** Mit optionals */ func optStringFunktion(xString: String?) -> String? { if let a = xString { println("\(a) wurde übergeben, ein Fehler!") return nil; } return "Alles gut!" } var neuerString: String if let x = optStringFunktion("ABC") { neuerString = x } else { println("Leider ist die erste Funktion fehlgeschlagen") } |
Wer es vielleicht nicht direkt erkannt hat, wird feststellen das wir im Fehlerfall ohne optionals zusätzlich einen Bool zurückliefern müssten. Man hätte auch auf die Stringlänge prüfen können, aber ein Fehler sollte auch eindeutig als Fehler erkennbar sein. Die Rückgabe mit dem Wert nil eignet sich dafür in Swift am besten.
In Verbindung mit Protokollen beziehungsweise in Verbindung mit dem „Delegation pattern“ spielen optionals erst richtig ihre Vorzüge aus. Dies wird ein wichtiger Bestandteil unseres folgenden Kapitel werden und schließt das Kapitel 2 hier ab.
Abschließendes zu Kapitel 2
Wir hoffen das Sie mit Kapitel 2 die wichtigsten Grundlagen der prozeduralen Programmierung verstanden haben. Mit dem aktuellen Fachwissen kann man jetzt noch keine komplette App bauen, aber mit dem nächsten Kapitel über Objektorientierung werden wir davon nur noch einen kleinen Schritt entfernt sein.
Wie immer hoffen wir für unseren Grundkurs auf Kritik und Verbesserungsvorschläge. Unser Ohr und E-Mail Postfach ist immer für Sie erreichbar.
Vielen Dank,
Stefan Popp & Ralf Peters
15 Comments
Hallo Swift-Blog,
Ich wollte fragen ob ihr vielleicht nach jedem Kapitel einen kleinen Test machen könntet, damit man einfach sicher sein kann, dass man alles verstanden hat .
Hallo Fillip,
ich finde das ist eine ganz hervorragende Idee für die Anfängerkapitel und ich werde das in die Wege leiten. Danke für dein Feedback und den guten Tip.
Liebe Grüße
Ralf
Hi,
Super BLOG habe jetzt alle Tutorial die auf Schrift sind durchgegcukt und finde ich sehr NICE gemacht.
Ich hoffe das es bald Kapitel 3 auch als Textform erscheinen wird. Denn ich finde Text besser als ein Video. Aber einige mögen es anders herum 🙂
Danke für alles Jungs.
LG
Achim
Danke für die Mühe!
Euer Tut ist wirklich sehr nett zu lesen, auch die Videos sind sehr gut gelungen.
Ich hoffe Ihr macht so weiter =)
MfG,
Stefan
Wir bleiben am Ball und sind im Moment dabei neue Inhalt für die Veröffentlichung vorzubereiten.
Vielen Dank für das Feedback!
Hallo,
wenn ich im Playground u.a. Code eingebe, bekomme ich fgolgende Fehlermeldung. meinString sei kein Bool und könne so nicht auf Wahrheitsgehalt geprüft werden, besser sei eine Überprüfung mit:
meinString != nil
var neuerString: String // Deklaration ohne Initialisierung
var meinString: String! = „Hallo“ // Hallo
meinString = nil // Variablen nil’len
// Prüfen und zuweisen
if meinString {
neuerString = meinString
}
Hat sich da was geändert? Bitte um eine kurze Erklärung.
Gruß Horst
Hallo Horst,
das Verhalten ist tatsächlich korrekt vom Compiler. Alternativ kannst du „Optional Binding“ nutzen, mit dieser Technik wird ein String nur dann in eine temporäre Variable gespeichert, wenn der Wert auch tatsächlich existiert.
Hier ein Beispiel:
var neuerString: String // Deklaration ohne Initialisierung
var meinString: String! = „Hallo“ // Hallo
meinString = nil // Variablen nil’len
// Prüfen und zuweisen
if let aString = meinString {
neuerString = aString
}
Viele Grüße,
Stefan
Hallo Stefan,
danke für die schnelle Antwort.
Gruß Horst
Hallo Stefan,
Eure Tutorials sowie auch die Videos sind wirklich super gut erklärt. Habe soeben euer Buch „Durchstarten mit Swift“ bei Amazon.de geordert.
Ich hoffe, dass Ihr die Video-Tutorials weiter macht und freue mich schon auf die nächsten Videos und auch Tutorials!
Viele Grüsse,
Peter
Hallo Peter,
vielen Dank für dein Kommentar und danke (!!!) für den Kauf des Buches! Es wird natürlich weiter gehen, aber das Berufsleben erdrückt mich gerade immens. Ich habe bereits neue Videos aufgenommen und müsste diese nur noch schneiden. Es wird aber glücklicherweise gerade ruhiger und ich konnte schon bei den ersten beginnen 🙂
Viele Grüße,
Stefan
Hallo Stefan,
ich bin vollkommener Swift-Neuling und bin gerade beim Basic Kapitel Schleifen angekommen. Wenn ich mich selbst im Playground ausprobiere, treffe ich immer wieder auf folgende Fehlermeldung:
„Statement cannot begin with a clossure expression“
Unten siehst du meinen Programmcode:
for var trial = 0.5: Double; trial < 200; trial + 1.5: Double {
println("That's my \(trial)")
}
Würde mich über eine Erklärung sehr freuen!
Gruß Julian
Hallo bin zwar auch relativ neu in Swift aber denke dein code sollte so aussehen….for var trail:Double = 0.5;trail<200.0;trail += 1.5{
println("That,s my \(trail)")
}
Hallo bin zwar auch relativ neu in Swift aber denke dein code sollte so aussehen….for var trail = 0.5; trail<200; trail += 1.5 {
println("That,s my \(trail)")
}
Wahnsinn! Danke für diesen tollen Beitrag! Hat mir sehr geholfen!
Das ist nach längerer Suche der erste Beitrag, der mir Optionals verständlich erklärt! Vielen Dank!