Optionals gehören zu den spannendsten Sprachmerkmalen in Swift und tragen einen enormen Teil dazu bei, die Sicherheit und Stabilität von Swift-Code zu gewährleisten. Da ist es bisweilen umso ärgerlicher, wenn man in einem Projekt neben Swift auch noch Objective-C einsetzt. Letzteres kennt nämlich ein solches Prinzip wie das der Optionals nicht, was zwar grundsätzlich dem Zusammenspiel der beiden Sprachen keinen Abbruch tut, aber die Verwendung von Objective-C-APIs aus Swift heraus unschön gestaltet. Was genau bei dieser Konstellation geschieht und wie man doch Optionals in Objective-C definieren (wenn auch nur in Swift nutzen) kann, verrate ich euch im Folgenden.
Das Standardverhalten von Objective-C
Alle Properties sowie Methodenparameter und -rückgabewerte, die in Objective-C deklariert sind, werden standardmäßig in Swift als Implicitly Unwrapped Optionals importiert. Dieses Verhalten soll anhand eines kleinen Beispiels erläutert werden. Hierfür folgt zunächst einmal ein kleines, in Objective-C geschriebenes Code-Snippet:
@interface Vehicle : NSObject
@property (copy) NSString *manufacturer;
- (NSDate *)manufacturingDate;
- (void)changePaintwork:(NSString *)paintwork;
@end
Es zeigt die simple Deklaration einer Klasse namens Vehicle
zur Abbildung von Fahrzeugen, die über eine Property für den Namen des Herstellers und zwei Methoden verfügt. Die erste liefert das Herstellungsdatum eines Fahrzeugs zurück, die zweite ändert die Lackierung auf einen gewünschten Wert.
Greift man auf eine solche Objective-C-API aus Swift heraus zu, wird diese wie folgt importiert:
class Vehicle: NSObject { var manufacturer: String! func manufacturingDate() -> Date! func changePaintwork(paintwork: String!) }
Alle Properties und Parameter werden als Implicitly Unwrapped Optionals deklariert, da Swift unmöglich wissen kann, ob einzelne Eigenschaften möglicherweise Pflicht (und damit keine Optionals) oder – im genau umgekehrten Fall – ob sie womöglich richtige Optionals sind und somit besser mit einem ?
statt dem !
deklariert werden sollten.
Die Lösung: Nullability
Erfreulicherweise hat Apple Objective-C dahingehend erweitert, dass man mithilfe neuer Schlüsselwörter in Objective-C-Code definieren kann, ob eine Property oder ein Parameter ein Optional ist oder nicht:
nonnull
: Die entsprechende Eigenschaft ist kein Optional und muss somit immer über einen validen Wert verfügen.nullable
: Die entsprechende Eigenschaft ist ein Optional und soll daher mit?
deklariert werden.
Bei Properties fügt man das gewünschte Schlüsselwort bei den Optionen hinzu, bei Methodenparametern und -rückgabewerten wandert es vor die jeweilige Typangabe. So kann das zuvor gezeigte Objective-C-Beispiel wie folgt optimiert werden:
@interface Vehicle : NSObject
@property (copy, nonnull) NSString *manufacturer;
- (nonnull NSDate *)manufacturingDate;
- (void)changePaintwork:(nullable NSString *)paintwork;
@end
So wird definiert, dass die Property manufacturer
und das Ergebnis der Methode manufacturingDate()
niemals optional sind und immer einen validen Wert zurückliefen, wohingegen der Parameter der Methode changePaintwork(paintwork:)
ein Optional ist (würde hier nil
übergeben, könnte beispielsweise die Standardlackierung auf das zugrundeliegende Fahrzeug angewendet werden). Der Import dieses optimierten Objective-C-Codes in Swift sieht nun wie folgt aus:
class Vehicle: NSObject {
var manufacturer: String
func manufacturingDate() -> Date
func changePaintwork(paintwork: String?)
}
Verzichtet man auf eines der beiden genannten Schlüsselwörter, greift die für Optionals dritte Option, wie wir es bereits im vorangegangenen Abschnitt gesehen haben: Die entsprechende Eigenschaft wird dann als Implicitly Unwrapped Optional importiert.
Ganze Bereiche als Optional deklarieren
Neben den Schlüsselwörtern nonnull
und nullable
bringt Objective-C inzwischen auch noch zwei Makros mit, mit deren Hilfe ihr alle Eigenschaften eines gesamten Bereichs als nichtoptional kennzeichnen könnt. In diesem Fall müsst ihr nur noch das nullable
-Schlüsselwort explizit für alle Eigenschaften innerhalb jenes Bereichs setzen, die optional sind; alle anderen werden – auch ohne Deklaration mittels nonnull
– automatisch als nicht optionale Elemente in Swift importiert.
Um einen solchen Bereich zu definieren, schreibt ihr zunächst den Befehl NS_ASSUME_NONNULL_BEGIN
, gefolgt von dem gewünschten Code. Am Ende des Blocks schließt ihr das ganze dann mittels NS_ASSUME_NONNULL_END
wieder ab. Das zuvor gezeigte Objective-C-Beispiel kann auf diese Art und Weise wie folgt abgewandelt werden (es kann dann auf die Verwendung des Schlüsselworts nonnull
verzichtet werden, was dafür sorgt, dass nur der Methodenparameter für changePaintwork(paintwork:)
noch explizit mittels nullable
gekennzeichnet werden muss):
NS_ASSUME_NONNULL_BEGIN
@interface Vehicle : NSObject
@property (copy) NSString *manufacturer;
- (NSDate *)manufacturingDate;
- (void)changePaintwork:(nullable NSString *)paintwork;
@end
NS_ASSUME_NONNULL_END
Fazit
Objective-C gehört – trotz der Attraktivität und vielen Vorzügen von Swift – noch immer zu einer in Apple-Projekten enorm häufig eingesetzten Programmiersprache (was nicht zuletzt auch mit vielen älteren Projekten zusammenhängt, die bereits vor dem Release von Swift entstanden sind). Umso schöner sind dann die Möglichkeiten, die es erlauben, Objective-C-Code dahingehend zu optimieren, dass er die speziellen Sprachfeatures von Swift wie beispielsweise die Optionals optimal unterstützt.
Zwar spielen die genannten Schlüsselwörter bei reinem Einsatz von Objective-C keine Rolle, erleichtern das Programmieren aber ungemein, sobald ein solches Projekt um Swift-Code ergänzt wird, der auf solche Objective-C-APIs zurückgreift.
Euer Thomas
Schreibe einen Kommentar