[vc_row][vc_column width=“1/6″][/vc_column][vc_column width=“2/3″][vc_column_text]Im Juni überraschte Apple die ganze Welt mit der Vorstellung einer neuer Programmiersprache: Swift. Seit der Veröffentlichung von iOS 8 im September 2014 liegt Swift in der Version 1.0 vor und ist offiziell für die iOS-Entwicklung nutzbar und seit der Veröffentlichung von Mac OS X 10.10 Yosemite im Oktober 2014 ist es auch für die Entwicklung von Anwendungen für Mac OS X nutzbar. Im Folgenden möchte ich Ihnen Apples neue Programmiersprache gerne vorstellen und beleuchten welche Vorteile sie verspricht.

Warum Swift?

Die Programmiersprache mit der üblicherweise für Mac OS X und iOS entwickelt wurde ist Objective-C. Diese ist jedoch schon über 30 Jahre alt und basiert auf der noch einmal zehn Jahre älteren Programmiersprache C. Die Informatik ist jedoch in den letzten 40 Jahren nicht stehen geblieben und so wurden viele neue Konzepte in Programmiersprachen erfunden und viele positive wie auch negative Erfahrungen mit bestehenden Programmiersprachen gemacht. Zwar ist auch Objective-C weiterentwickelt worden (so stellte Apple z.B. 2006 Objective-C 2.0 vor), einige grundlegende Designentscheidungen von C und auch Objective-C lassen sich jedoch nicht mehr im Nachhinein ändern. Außerdem wirkten viele der moderneren Sprachfeatures von Objective-C, wie z.B. Blocks doch eher wie nachträglich hinzugefügt und nicht wie natürliche Konstrukte der Sprache. Entsprechend warnte John Siracusa Apple schon 2005 davor, dass sie den Anschluss an die modernen Programmiersprachen verlieren, wenn sie weiter auf Objective-C setzen.

Eine Programmiersprache muss aber nicht nur moderne Programmierkonzepte unterstützen, sie sollte gleichzeitig auch wenig Overhead erzeugen, also für schnelle Programme sorgen. Insbesondere wenn mit dieser Programmiersprache Apps für Embedded Devices wie z.B. Smartphones oder die angekündigte Apple Watch geschrieben werden, die durch ihre Größe und Anforderungen an geringen Energieverbrauch nur langsamere Prozessoren einsetzen können als Desktop-PCs. Genau diesen Spagat zwischen Performance und modernen Features und geringerer Fehleranfälligkeit einer Programmiersprache versucht Apple mit Swift zu schaffen. Nach Apples eigener Aussage sind die Kernfeatures von Swift: „Modern“, „Safe“, „Fast and Powerful“.

Modern

Swift enthält eine Reihe moderner Programmierkonzepte die schon aus anderen Sprachen wie z.B. Ruby oder C# bekannt sind. Im typischen Apple-Stil hat die Firma aus Cupertino sich hier bei den aktuellen Technologien umgesehen, sich die gut funktionierenden Konzepte herausgepickt und ein überzeugendes Gesamtpaket geschnürt, bei dem viele alte Zöpfe abgeschnitten werden. So ist Swift eine Multiparadigmen-Programmiersprache geworden, neben der bekannten objektorientierten Programmierung unterstützt Swift auch viele Konzepte der sogenannten funktionalen Programmierung.

Funktionale Programmierung

Außer anonymen Funktionen, sogenannten Lambda-Ausdrücken, und der Behandlung von Funktionen als Datentypen erster Klasse werden auch sogenannte Funktionen höherer Ordnung unterstützt, also Funktionen deren Parameter wiederum andere Funktionen sind. Um z.B. für eine Liste von Zahlen die jeweiligen Quadratzahlen zu berechnen könnte in Objective-C folgender Code genutzt werden:

[code lang=“objc“]
NSArray *numbers = @[@1, @2, @3, @4, @5, @6];
NSMutableArray *squaredNumbers = [NSMutableArray array];
for (NSNumber *number in numbers)
{
NSNumber *squaredResult = [NSNumber numberWithInt:[number intValue] * [number intValue]];
[squaredNumbers addObject:squaredResult];
}
NSLog(@“squared numbers: %@“, squaredNumbers);
[/code]

Im Gegensatz dazu sieht der Code in Swift deutlich übersichtlicher und klarer aus und benutzt die Funktion höherer Ordnung map.

[code lang=“objc“]
let numbers = [1, 2, 3, 4, 5, 6]
let squaredNumbers = numbers.map({x in x * x})
println(„square numbers: \(squaredNumbers)“)
[/code]

So werden Operationen die früher oft ein Schleifenkonstrukt brauchten nun oft zu Einzeilern. Das ist zum einen schneller und drückt zum anderen meist die Intention des Programmierers viel klarer aus. Um z.B. alle Zahlen in einer Liste aufzusummieren könnte folgender Code verwendet werden:

[code lang=“objc“]
let sum = numbers.reduce(0, +)
[/code]

Generische Programmierung

Neben den funktionalen und objektorientierten Programmieransätzen unterstützt Swift auch noch die sogenannte Generische Programmierung. Mit Hilfe der generischen Programmierung können viele Algorithmen soweit abstrahiert werden, dass sie auf allgemeinen Datentypen implementiert werden und nicht für jeden Datentyp erneut geschrieben werden müssen. Ein Beispiel dafür ist z.B. eine Funktion, die das Maximum zweier Zahlen berechnet. In einer nicht-generischen Sprache wie C müsste dieses Funktion für jeden Datentyp einzeln implementiert werden:

[code lang=“c“]
int maxInt(int a, int b) {
if (a > b) {
return a;
}
return b;
}

float maxFloat(float a, float b) {
if (a > b) {
return a;
}
return b;
}
[/code]

Wie man leicht sieht, ist die Implementierung selbst aber sehr ähnlich, es gibt also viel wiederkehrenden Code. Sprachen die Interfaces unterstützen, wie z.B. Objective-C oder Java, können die Implementierung vereinfach, indem die Funktion einmal für alle Klassen, die ein bestimmtes Interface implementieren, implementiert wird. Das könnte z.B. so aussehen:

[code lang=“objc“]
id<Comparable> max(id<Comparable> a, id<Comparable> b) {
if ([a compare:b] == NSOrderedDescending) {
return a;
}
return b;
}
[/code]

Dieser Ansatz birgt jedoch zwei Probleme. Zum einen geht die exakte Typenformation verloren. Der Vergleich zweier Strings könnte dann z.B. so aussehen:

[code lang=“objc“]
id<Comparable> result = max(@“hallo“, @“welt“);
[/code]

In diesem Fall ist das Ergebnis vom Typ „Comparable“, es ist aber für den Compiler nicht erkennbar, ob das Ergebnis eine NSNumber oder ein NSString ist. Hier muss also ein expliziter Cast erfolgen. Dies ist zwar störend aber kein besonders großes Problem. Ein viel größeres Problem ist, dass diese allgemeine max-Funktion auch mit Argumenten aufgerufen werden kann, die keinen Sinn machen:

[code lang=“objc“]
id<Comparable> result = max(@“hallo“, @1);
[/code]

Hier wird also der String „hallo“ mit der Zahl 1 verglichen. Das ergibt keinen Sinn, da aber beide „Comparable“ sind ist der Aufruf gültig und der Compiler kann diesen Fehler nicht erkennen.

Swift und andere generische Programmiersprachen wie z.B. Java oder C++ lösen das Problem indem sie eine Typvariable einführen:

[code lang=“objc“]
func max<T:Comparable>(a:T, b:T) -> T {
if (a > b) {
return a
}
return b
}
[/code]

Hier wird also festgelegt, dass nicht nur beide Argumente „Comparable“ sein müssen, sondern auch noch vom selben Typ. Damit wird das Problem aus dem vorherigen Beispiel umgangen, da die Funktion nicht mehr mit zwei verschiedenen Typen aufgerufen werden kann. Auch ist der Rückgabetyp nun über die Typvariable spezifiziert und der Compiler weiß, dass wenn zwei „int“-Werte verglichen werden das Ergebnis auch ein int ist.

Safe

Swift ist in vielen Aspekten auch eine deutlich sicherere Sprache als Objective-C. Hierbei meint „sicher“ vor allem dass der/die Programmierer/in öfter als in anderen Sprachen davor geschützt wird Fehler zu machen und insbesondere häufige Fehler die zum Absturz eines Programmes führen können öfter verhindert werden. Gerade durch solche Ausnahmesituationen, wie sie bei Abstürze auftreten, entstehen aber oft Sicherheitslücken, weshalb bei Programmen die mit Swift geschrieben wurden die berechtige Hoffnung besteht, dass diese auch im engeren „Security“-Sinne sicherer werden. Diese erhöhte Sicherheit wird durch verschiedene Aspekte erreicht:

Statische und starke Typisierung

Swift ist eine statisch typisierte Programmiersprache. Das bedeutet, dass die Typen der genutzten Variablen schon zum Zeitpunkt der Kompilierung festgelegt und geprüft werden und nicht erst zur Laufzeit. Dies erlaubt es dem Compiler viele Fehler direkt zu erkennen und darauf hinzuweisen noch bevor das Programm das erste Mal ausgeführt wird. Außerdem ist Swift eine recht stark typisierte Sprache, das bedeutet nur wenige Typumwandlung passieren automatisch, fast alle Typumwandlungen müssen explizit passieren. Auch dadurch werden viele Flüchtigkeitsfehler beim Programmieren vermieden.

Kein Null-Pointer by Default

Eine der gravierendsten Änderungen in Swift ist die Abschaffung von Null-Pointern an vielen verschiedenen Stellen. Zwar kann einer Variable in Swift noch der Wert null bzw. nil zugewiesen werden, aber nur dann, wenn diese explizit als „optional“ markiert wurde. Eine Variable vom Typ String kann somit nicht den Wert nil annehmen, dies geht nur wenn die Variable vom Typ String? bzw. Optional<String> ist. Somit wird die Tatsache ob ein Wert nil sein kann explizit gemacht und ist vom Compiler statisch überprüfbar, was wiederum die Möglichkeit bietet viele potentielle Null-Pointer-Fehler schon zur Zeit der Kompilierung zu erkennen.

Diese Änderung hat das Potential eine ganze Klasse von häufigen Fehlern in der Programmierung auszumerzen. So sagt z.B. der bekannte britische Informatiker Tony Hoare über seine Entscheidung Null-Referenzen in ALGOL W zu erlauben, es sei ein „billion dollar mistake“ gewesen, da daraus sehr viele Fehler entstanden seien die Kosten in Milliardenhöhe verursacht hätten. Dieses Bild bestätigt sich bei einem Blick auf die Fehler die bei Sicherheitsupdates behoben werden. So sind zum Beispiel 2 der 20 behobenen Fehler im Sicherheitsupdate zu Max OS X 10.9.4 Null-Pointer-Fehler, die mit Swift vermutlich so nicht möglich wären bzw. schon zur Kompilierungszeit gefunden worden wären. Insgesamt hätten sogar bis zu 13 der 20 in 10.9.4 behobenen Sicherheitslücken durch den Einsatz von Swift verhindert worden sein können (2x Null-Pointer-Fehler, 2x Integer Over-/Underflow, 9x Buffer Overrun/Bounds Checking).

Nicht-Veränderbarkeit von Daten

Die Nicht-Veränderbarkeit oder auch Immutability eines Objektes oder einer Variable ist ein weiteres Konzept das aus der funktionalen Programmierung übernommen wurde. Hier sind alle Daten standardmäßig unveränderlich, es können immer nur neue Daten mit anderen Werten erzeugt werden. Dies macht es in vielen Fällen einfacher Schlussfolgerungen über das Verhalten des Programmcodes zu ziehen und ist daher ebenso ein oft genutztes Konzept zur Verhinderung von Programmierfehlern. Zwar sind in Swift nicht alle Variablen und Objekte unveränderlich, es ist jedoch durch das Schlüsselwort „let“ sehr einfach eine Datenstruktur als unveränderlich zu markieren. Auch hier kann der Compiler dann wiederum prüfen ob das Versprechen der Unveränderlichkeit eingehalten wird und so für besser geprüften Programmcode sorgen.

Fast and Powerful

Wie schon oben erwähnt ist neben der Ausdrucksstärke und Sicherheit einer Programmiersprache auch die Performance der resultierenden Apps von sehr großer Bedeutung, gerade im Kontext von Apples Produkten, die oft auf kleine stromsparende Prozessoren setzen. Außerdem hat Apple mit Swift den Anspruch geäußert eine Programmiersprache zu entwickeln die auch für die interne Programmierung von Betriebssystem genutzt werden kann und auch hier ist für Kernfunktionen, die millionenfach pro Minute aufgerufen werden, die Performance natürlich von allergrößter Bedeutung. Daher werden die entsprechenden Teile des Betriebssystems auch heute noch oft in C oder C++ geschrieben werden. Es ist somit klar, dass in Swift geschriebene Anwendungen auch schnell laufen müssen. Bei genauerer Betrachtung stellt sich heraus, dass Swift auch hier großes Potential hat, aber es wohl noch etwas dauert bis dieses Potential voll ausgeschöpft wird.

Virtual Functions statt Message Passing

Einer der Performance-Nachteile von Objective-C ist, dass Methodenaufrufe nicht direkte Funktionsaufrufe auf der Ebene des vom Compiler generierten Codes sind sondern stattdessen Strings als Nachrichten zwischen Objekten geschickt werden. Das empfangende Objekt prüft dann, welche Nachricht tatsächlich empfangen wurde und ruft die dazu passende Funktion auf. Dies ermöglicht einige der mächtigen Einsatzmöglichkeiten von Objective-C, aber das Nachschlagen der passenden Funktion an Hand eines Strings ist potentiell langsamer und wird oft nicht benötigt. Im Gegensatz dazu nutzt Swift, ähnlich wie C++, in vielen Fällen sogenannte virtuelle Methoden um den Methodenaufruf abzubilden. Hierbei sind die zur Verfügung stehenden Methoden in einer fest indizierten Tabelle gespeichert, was das bestimmen der aufzurufenden Methode deutlich schneller macht.

Structs

In Swift sind Verbunddatentypen, sogenannte structs, deutlich mächtiger, da sie beispielsweise Methoden haben können und somit an deutlich mehr Stellen eingesetzt werden können. Der Vorteil von Verbunddatentypen gegenüber Klassen ist, dass diese als sogenannte Value Types angesehen werden, wohingegen Klassen Reference Types sind. Instanzen eines Value Types werden üblicherweise auf dem Stack gespeichert und werden bei der Übergabe an eine andere Funktion kopiert. Instanzen eines Reference Types werden hingegen auf dem Heap gespeichert und bei der Übergabe an eine andere Funktion wird nur die Referenz zur selben Instanz des Objekts übergeben. Das macht es notwendig für Referenztypen komplexe Speicherverwaltungsstrategien zu entwickeln um zu entscheiden, wann die zugehörigen Daten wieder freigegeben werden. Dies ist bei Value Types nicht notwendig, sobald die entsprechende Funktion beendet ist, werden alle Daten die in der Funktion genutzt wurden wieder freigegeben.

Ein anderer Vorteil von Verbunddatentypen ist, dass diese keine Vererbung unterstützen. Somit kann schon zum Zeitpunkt der Kompilierung entschieden werden, welche Methode aufgerufen wird, da im Gegensatz zu Klassen mit Vererbungshierarchien keine sogenannte Dynamische Bindung von Methoden möglich ist. Hierdurch werden Funktionsaufrufe noch einmal schneller. Somit können durch den Einsatz von Verbunddatentypen im Gegensatz zu Klassen oft erstaunliche Performanceverbesserungen erreicht werden. Joseph Lord hat beispielsweise in einigen Experimenten mit Verbundtypen und Klassen festgestellt, dass das Ersetzen einer Klasse durch einen Verbunddatentypen den betroffenen Code 54x schneller machte.

Der Hinreichend Intelligente Compiler

Wann immer eine moderne Programmiersprache mit einer älteren low-level Programmiersprache verglichen wird, wird irgendwann das Argument des „hinreichend intelligenten Compilers“ auftauchen. Die Argumentation ist in diesem Fall, dass obwohl die low-level Programmiersprache für einzelne Programmierkonstrukte schneller ist, ein hinreichend intelligenter Compiler die in der modernen Programmiersprache zusätzlich verfügbaren Informationen nutzen kann um Teile des Codes deutlich effizienter zu gestalten und Optimierungsstrategien anzuwenden, die bei einer primitiven Programmiersprache nicht möglich wären. Bleibt dieses Argument in vielen Fällen auch ein theoretisches, so hat es für Swift tatsächlich praktische Relevanz und zwar aus zwei Gründen: Erstens enthält Swift viele abstrakte Konstrukte, die die Semantik der Sprache einschränken und damit solche Optimierungen theoretisch möglich machen. Zweitens ist einer der Schöpfer von Swift, Chris Lattner, tatsächlich auch Autor des LLVM-Projektes und des Clang-Compilers die aktuell eingesetzt werden um C, Objective-C, und C++ zu kompilieren und zu optimieren. Man darf also davon ausgehen, dass viele der besonderen Konstrukte in Swift nicht nur akademische Wunschträume sind, sondern sich tatsächlich in der Praxis auch deutlich besser automatisiert optimieren lassen.

Trotz allem muss man sagen, dass es zum aktuellen Zeitpunkt noch viele Beschwerden darüber gibt, dass Swift langsam sei. Es steckt jedoch viel Potenzial in der Sprache und es scheint leicht vorstellbar, dass Apple durch kontinuierliche Weiterentwicklung der Swift-Compiler-Wekzeuge die Sprache wie versprochen optimieren und deutlich performanter als Objective-C machen kann. Vielleicht wird diese Entwicklung sogar in einigen Jahren so weit gehen, dass sich Swift zu einer ernsthaften Konkurrenz zu C entwickelt. Aktuell muss noch kein Objective-C-Programmierer fürchten aus der Entwicklung mit Objective-C Nachteile zu haben, stattdessen wird sich zum jetzigen Zeitpunkt seine größere Erfahrung mit der Sprache positiv auswirken. Ich nehme jedoch an, dass sich innerhalb der nächsten zwei Jahre das Blatt wendet und Objective-C nur noch für alte Projekte oder für Spezialanwendungen, bei denen die dynamische Natur von Objective-C nützlich ist, verwendet wird.[/vc_column_text][/vc_column][vc_column width=“1/6″][/vc_column][/vc_row]