Einführung
Swift ist eine neue Programmiersprache für die Entwicklung von iOS- und OS X-Anwendungen. Sie basiert auf C und Objective-C, jedoch ohne die Kompatibilitätseinschränkungen von C. Swift verwendet sichere Programmiermuster und fügt moderne Funktionen hinzu, um die Programmierung einfacher, flexibler und unterhaltsamer zu machen. Die Schnittstelle basiert auf den weithin beliebten Frameworks Cocoa und Cocoa Touch und zeigt eine neue Richtung in der Softwareentwicklung auf.

Swift wird seit vielen Jahren entwickelt. Apple baute auf vorhandenen Compilern, Debuggern und Frameworks als zugrunde liegender Architektur auf. Es verwendet ARC (Automatic Reference Counting), um die Speicherverwaltung zu vereinfachen. Unser Framework-Stack basierte schon immer auf Cocoa. Objective-C entwickelte sich weiter, um Blocks, Collection-Literale und Module zu unterstützen, wodurch moderne Sprach-Frameworks ohne tiefgreifende Beteiligung genutzt werden können. Dank dieser Grundlagenarbeit wurde es möglich, eine neue Programmiersprache in die Apple-Softwareentwicklung einzuführen.
Objective-C-Entwickler werden Swift vertraut finden. Swift übernimmt die benannten Parameter und das dynamische Objektmodell von Objective-C. Es bietet Interoperabilität mit den Cocoa-Frameworks und Mix-and-Match. Auf diesen Grundlagen aufbauend führt Swift viele neue Funktionen ein und kombiniert prozedurale und objektorientierte Funktionalität.
Swift ist auch für neue Programmierer freundlich. Es ist eine Systemprogrammiersprache in Industriequalität und dennoch so freundlich wie eine Skriptsprache. Es unterstützt Playgrounds, mit denen Programmierer mit einem Stück Swift-Code experimentieren und die Ergebnisse sofort sehen können, ohne den Aufwand, eine App zu erstellen und auszuführen.
Swift integriert Ideen aus modernen Programmiersprachen und die Weisheit von Apples Engineering-Kultur. Der Compiler ist auf Leistung optimiert, während die Sprache auf Entwicklung optimiert ist, ohne eines von beiden zu beeinträchtigen. Sie können mit "Hello, world" beginnen und zu einem ganzen System übergehen. All dies macht Swift zu einer Quelle der Innovation für Apple-Softwareentwickler.
Swift ist eine fantastische Möglichkeit, iOS- und OS X-Anwendungen zu schreiben, und es wird weiterhin neue Funktionen einführen. Wir können es kaum erwarten zu sehen, was Sie damit erschaffen.
Erste Schritte mit Swift
Das Erlernen einer neuen Sprache sollte mit dem Ausgeben von "Hello, world" beginnen. In Swift ist das nur eine Zeile:
println("Hello, world")
Wenn Sie schon C- oder Objective-C-Code geschrieben haben, kommt Ihnen diese Syntax bekannt vor. In Swift ist dies ein vollständiges Programm. Sie müssen keine separate Bibliothek für Ein-/Ausgabe und Zeichenkettenverarbeitung importieren. Code im globalen Gültigkeitsbereich ist der Einstiegspunkt für das Programm, also müssen Sie keine main()-Funktion schreiben. Sie müssen auch nicht nach jeder Anweisung ein Semikolon schreiben. Diese Einführung bietet genügend Informationen, um Ihnen zu zeigen, wie Sie eine Programmieraufgabe erledigen. Machen Sie sich keine Sorgen über Dinge, die Sie noch nicht verstehen; alles, was hier nicht klar erklärt wird, wird später in diesem Buch ausführlich behandelt.
Hinweis:Als bewährte Vorgehensweise können Sie dieses Kapitel in einem Xcode-Playground öffnen. Playgrounds ermöglichen es Ihnen, Code zu bearbeiten und die Ergebnisse sofort zu sehen.
Einfache Werte
Verwenden Sie let, um eine Konstante zu definieren, und var, um eine Variable zu definieren. Der Wert einer Konstante muss zur Kompilierungszeit nicht bekannt sein, aber Sie müssen ihn mindestens einmal zuweisen. Das bedeutet, Sie können eine Konstante verwenden, um einen Wert zu benennen, den Sie einmal bestimmen, aber an vielen Stellen verwenden.
var myVariable = 42 myVariable = 50 let myConstant = 42
Hinweis:Konstantendefinitionen ähneln hier Variablen in funktionalen Programmiersprachen: Einmal zugewiesen, lassen sie sich nicht ändern. Verwenden Sie sie oft; das ist gut für Sie.
Eine Konstante oder Variable muss denselben Typ wie der Wert haben, den Sie zuweisen. Daher müssen Sie den Typ nicht explizit angeben. Geben Sie einfach einen Wert an, um eine Konstante oder Variable zu erstellen, und lassen Sie den Compiler ihren Typ ableiten. Im obigen Beispiel leitet der Compiler ab, dass myVariable vom Typ Integer ist, weil ihr Anfangswert eine Ganzzahl ist.
Hinweis:Typen sind an Variablennamen gebunden, was dies zu einer statisch typisierten Sprache macht. Das hilft bei der statischen Optimierung. Dies unterscheidet sich von Python, JavaScript und anderen. Wenn der Anfangswert nicht genügend Informationen liefert (oder wenn es keinen Anfangswert gibt), können Sie den Typ nach dem Variablennamen schreiben, getrennt durch einen Doppelpunkt.
let imlicitInteger = 70 let imlicitDouble = 70.0 let explicitDouble: Double = 70
Werte werden niemals implizit in andere Typen konvertiert. Wenn Sie einen Wert in einen anderen Typ konvertieren müssen, konstruieren Sie explizit eine Instanz des gewünschten Typs.
let label = "The width is " let width = 94 let widthLabel = label + String(width)
Es gibt eine noch einfachere Möglichkeit, Werte in Zeichenketten einzufügen: Schreiben Sie den Wert in Klammern und setzen Sie einen Backslash ("\") vor die Klammern. Zum Beispiel:
let apples = 3 let oranges = 5 let appleSummary = "I have \(apples) apples." let fruitSummary = "I have \(apples + oranges) pieces of fruit."
Um ein Array oder Dictionary zu erstellen, verwenden Sie eckige Klammern "[]", und greifen Sie auf ihre Elemente zu, indem Sie den Index oder Schlüssel in Klammern schreiben.
var shoppingList = ["catfish", "water", "tulips", "blue paint"] shoppingList[1] = "bottle of water" var occupations = [ "Malcolm": "Captain", "Kaylee": "Mechanic", ] occupations["Jayne"] = "Public Relations"
Um ein leeres Array oder Dictionary zu erstellen, verwenden Sie die Initialisierungssyntax:
let emptyArray = String[]() let emptyDictionary = Dictionary<String, Float>()
Wenn Typinformationen nicht abgeleitet werden können, können Sie ein leeres Array als "[]" und ein leeres Dictionary als "[:]" schreiben — zum Beispiel, wenn Sie eine Variable eines bekannten Typs festlegen und sie als Argument an eine Funktion übergeben.
shoppingList = [] //去购物并买些东西
Kontrollfluss
Verwende if und switch für Bedingungen. Verwende for-in, for, while und do-while für Schleifen. Klammern sind nicht erforderlich, aber geschweifte Klammern um den Rumpf sind erforderlich.
let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
if score > 50 {
teamScores += 3
} else {
teamScores += 1
}
}
teamScore
In einer if-Anweisung muss die Bedingung ein Boolean-Ausdruck sein — das bedeutet, if score { ... } ist ein Fehler und kann nicht implizit mit 0 verglichen werden. Du kannst if und let zusammen verwenden, um mit Werten zu arbeiten, die möglicherweise fehlen. Diese Werte sind optional. Ein optionaler Wert kann einen Wert enthalten oder nil enthalten, um anzuzeigen, dass der Wert noch nicht existiert. Schreibe ein Fragezeichen "?" nach dem Typ, um den Wert als optional zu markieren.
var optionalString: String? = "Hello"
optionalString == nil
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
Hinweis:Wenn der optionale Wert nil ist, ist die Bedingung falsch und der Code in geschweiften Klammern wird übersprungen. Andernfalls wird der optionale Wert entpackt und einer Konstanten zugewiesen, wodurch der entpackte Wert innerhalb des Codeblocks verfügbar wird.
switch unterstützt viele Arten von Daten und eine Vielzahl von Vergleichen — es ist nicht auf Ganzzahlen und Gleichheitstests beschränkt.
let vegetable = "red pepper"
switch vegetable {
case "celery":
let vegetableComment = "Add some raisins and make ants on a log."
case "cucumber", "watercress":
let vegetableComment = "That would make a good tea sandwich."
case let x where x.hasSuffix("pepper"):
let vegetableComment = "Is it a spicy \(x)?"
default:
let vegetableComment = "Everything tastes good in soup."
}
Hinweis:
Nachdem ein passender case ausgeführt wurde, verlässt das Programm das switch, anstatt mit dem nächsten case fortzufahren. Du benötigst also kein break mehr, um das switch zu verlassen.
Du kannst for-in verwenden, um über jedes Element in einem Dictionary zu iterieren, wobei du ein Paar von Namen für jedes Schlüssel-Wert-Paar angibst.
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
}
}
}
Verwende while, um einen Codeblock zu wiederholen, bis sich die Bedingung ändert. Die Schleifenbedingung kann am Ende stehen, um sicherzustellen, dass die Schleife mindestens einmal ausgeführt wird.
var n = 2
while n < 100 {
n = n * 2
}
n
var m = 2
do {
m = m * 2
} while m < 100
m
Du kannst in einer Schleife einen Index führen, indem du ".." verwendest, um einen Bereich von Indizes anzugeben, oder explizit einen Anfangswert, eine Bedingung und einen Inkrementwert deklarierst. Diese beiden Schleifen bewirken dasselbe:
var firstForLoop = 0
for i in 0..3 {
firstForLoop += i
}
firstForLoop
var secondForLoop = 0
for var i = 0; i < 3; ++i {
secondForLoop += 1
}
secondForLoop
Ein mit .. konstruierter Bereich ignoriert den höchsten Wert, während ein mit ... konstruierter Bereich beide Werte einschließt.
Funktionen und Closures
Verwende func, um eine Funktion zu deklarieren. Rufe eine Funktion mit ihrem Namen auf, gefolgt von einer in Klammern gesetzten Argumentliste. Verwende ->, um die Parameternamen und den Rückgabetyp zu trennen.
func greet(name: String, day: String) -> String {
return "Hello \(name), today is \(day)."
}
greet("Bob", "Tuesday")
Verwende ein Tupel, um mehrere Werte zurückzugeben.
func getGasPrices() -> (Double, Double, Double) {
return (3.59, 3.69, 3.79)
}
getGasPrices()
Eine Funktion kann eine variable Anzahl von Argumenten akzeptieren, die in einem Array gesammelt werden.
func sumOf(numbers: Int...) -> Int {
var sum = 0
for number in numbers {
sum += number
}
return sum
}
sumOf()
sumOf(42, 597, 12)
Funktionen können verschachtelt sein. Eine verschachtelte Funktion kann auf die Variablen der Funktion zugreifen, in der sie definiert ist. Du kannst verschachtelte Funktionen verwenden, um Code zu organisieren, der ansonsten zu lang oder zu komplex wäre.
func returnFifteen() -> Int {
var y = 10
func add() {
y += 5
}
add()
return y
}
returnFifteen()
Funktionen sind erstklassige Typen. Das bedeutet, dass eine Funktion eine andere Funktion zurückgeben kann.
func makeIncrementer() -> (Int -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)
Eine Funktion kann andere Funktionen als Argumente entgegennehmen.
func hasAnyMatches(list: Int[], condition: Int -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
}
}
return false
}
func lessThanTen(number: Int) -> Bool {
return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(numbers, lessThanTen)
Funktionen sind eigentlich ein Spezialfall von Closures. Du kannst eine Closure ohne Namen schreiben, indem du sie einfach in geschweifte Klammern setzt. Verwende in, um die Argumente und den Rückgabewert vom Rumpf zu trennen.
numbers.map({
(number: Int) -> Int in
let result = 3 * number
return result
})
Es gibt mehrere Möglichkeiten, Closures zu schreiben. Wenn der Typ einer Closure bereits bekannt ist, etwa für einen Callback, kannst du ihre Parameter und ihren Rückgabetyp oder beides weglassen. Eine Closure mit einem einzelnen Ausdruck kann ihren Wert direkt zurückgeben.
numbers.map({number in 3 * number})
Du kannst auf ein Argument über eine Zahl statt über einen Namen verweisen, was für sehr kurze Closures nützlich ist. Eine Closure, die als letztes Argument an eine Funktion übergeben wird, kann nach den Klammern des Funktionsaufrufs platziert werden.
sort([1, 5, 3, 12, 2]) { $0 > $1 }
Objekte und Klassen
Verwende class, um eine Klasse zu erstellen. Eine Eigenschaftsdeklaration ist einfach eine Konstanten- oder Variablendeklaration im Klassenkontext. Methoden und Funktionen werden auf die gleiche Weise geschrieben.
class Shape {
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
Erstelle eine Instanz einer Klasse, indem du Klammern nach dem Klassennamen setzt. Verwende die Punkt-Syntax, um auf die Eigenschaften und Methoden der Instanz zuzugreifen.
var shape = Shape() shape.numberOfSides = 7 var shapeDescription = shape.simpleDescription()
Dieser Version der Shape-Klasse fehlt etwas Wichtiges: ein Initialisierer, um die Klasse beim Erstellen einer Instanz einzurichten. Verwende init, um einen zu erstellen.
class NamedShape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A Shape with \(numberOfSides) sides."
}
}
Hinweis:self wird verwendet, um die Eigenschaft name vom Argument name zu unterscheiden. Initialisierer werden wie Funktionen deklariert, außer dass sie eine Instanz der Klasse erstellen. Jede Eigenschaft benötigt einen zugewiesenen Wert, entweder in ihrer Deklaration oder im Initialisierer.
Verwende deinit, um einen Deinitialisierer zu erstellen, der beim Zerstören des Objekts Aufräumarbeiten ausführt.
Unterklassen enthalten ihren Superklassennamen, getrennt durch einen Doppelpunkt. Wenn von der standardmäßigen Wurzelklasse geerbt wird, muss keine Superklasse deklariert werden, du kannst sie also weglassen.
Methoden in einer Unterklasse können die Implementierung der Superklasse überschreiben, indem sie mit override markiert werden. Ohne override behandelt der Compiler dies als Fehler. Der Compiler prüft außerdem, dass mit override markierte Methoden tatsächlich eine Methode der Superklasse überschreiben.
class Square: NamedShape {
var sideLength: Double
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 4
}
func area() -> Double {
return sideLength * sideLength
}
override func simpleDescription() -> String {
return "A square with sides of length \(sideLength)."
}
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()
Hinweis:Eigenschaften können Getter und Setter haben.
class EquilateralTriangle: NamedShape {
var sideLength: Double = 0.0
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 3
}
var perimeter: Double {
get {
return 3.0 * sideLength
}
set {
sideLength = newValue / 3.0
}
}
override func simpleDescription() -> String {
return "An equilateral triangle with sides of length \(sideLength)."
}
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
triangle.perimeter
triangle.perimeter = 9.9
triangle.sideLength
Im Setter für perimeter hat der neue Wert den impliziten Namen newValue. Du kannst bei Bedarf einen abweichenden Namen in Klammern nach set angeben.
Hinweis:Der Initialisierer für EquilateralTriangle hat drei verschiedene Schritte:
- Festlegen der Werte von Eigenschaften
- Aufrufen des Initialisierers der Superklasse
- Ändern des Wertes einer von der Superklasse definierten Eigenschaft und Hinzufügen zusätzlicher Arbeit, um hier ebenfalls Methoden, Getter und Setter zu verwenden.
Wenn du keine berechnete Eigenschaft brauchst, aber trotzdem Arbeit ausführen möchtest, nachdem ein Wert gesetzt wurde, verwende willSet und didSet. Zum Beispiel stellt die folgende Klasse sicher, dass die Seitenlänge ihres Dreiecks gleich der Seitenlänge des Rechtecks ist.
class TriangleAndSquare {
var triangle: EquilaterTriangle {
willSet {
square.sideLength = newValue.sideLength
}
}
var square: Square {
willSet {
triangle.sideLength = newValue.sideLength
}
}
init(size: Double, name: String) {
square = Square(sideLength: size, name: name)
triangle = EquilaterTriangle(sideLength: size, name: name)
}
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
triangleAndSquare.square.sideLength
triangleAndSquare.triangle.sideLength
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
triangleAndSquare.triangle.sideLength
Es gibt einen wichtigen Unterschied zwischen Klassenmethoden und Funktionen. Parameternamen von Funktionen werden nur innerhalb der Funktion verwendet, aber Methodenparameternamen können auch beim Aufrufen der Methode verwendet werden (außer dem ersten Parameter). Standardmäßig hat eine Methode einen Parameter mit demselben Namen, und beim Aufruf ist es der Parameter selbst. Du kannst einen zweiten Namen angeben, der innerhalb der Methode verwendet wird.
class Counter {
var count: Int = 0
func incrementBy(amount: Int, numberOfTimes times: Int) {
count += amount * times
}
}
var counter = Counter()
counter.incrementBy(2, numberOfTimes: 7)
Bei der Arbeit mit optionalen Werten kannst du "?" vor einen Operator schreiben, ähnlich wie bei Methoden und Eigenschaften. Wenn der Wert vor "?" bereits nil ist, wird alles nach "?" automatisch ignoriert, und der gesamte Ausdruck ist nil. Andernfalls wird der optionale Wert entpackt, und alles nach "?" wirkt auf den entpackten Wert. In beiden Fällen ist der Wert des gesamten Ausdrucks ein optionaler Wert.
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square") let sideLength = optionalSquare?.sideLength
Aufzählungen und Strukturen
Verwende enum, um eine Aufzählung zu erstellen. Wie Klassen und andere benannte Typen können Aufzählungen Methoden haben.
enum Rank: Int {
case Ace = 1
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King
func simpleDescrition() -> String {
switch self {
case .Ace:
return "ace"
case .Jack:
return "jack"
case .Queen:
return "queen"
case .King:
return "king"
default:
return String(self.toRaw())
}
}
}
let ace = Rank.Ace
let aceRawValue = ace.toRaw()
Im obigen Beispiel ist der Typ des Rohwerts Int, daher musst du nur den ersten Rohwert angeben. Die nachfolgenden Rohwerte werden der Reihe nach zugewiesen. Du kannst auch Zeichenketten oder Gleitkommazahlen als Rohwerte einer Aufzählung verwenden.
Verwende die Funktionen toRaw und fromRaw, um zwischen Rohwerten und Aufzählungswerten zu konvertieren.
if let convertedRank = Rank.fromRaw(3) {
let threeDescription = convertedRank.simpleDescription()
}
Die Memberwerte einer Aufzählung sind tatsächliche Werte, nicht auf irgendeine andere Weise geschriebene Rohwerte. Tatsächlich gibt es in manchen Fällen keinen Rohwert, nämlich dann, wenn du keinen angibst.
enum Suit {
case Spades, Hearts, Diamonds, Clubs
func simpleDescription() -> String {
switch self {
case .Spades:
return "spades"
case .Hearts:
return "hearts"
case .Diamonds:
return "dismonds"
case .Clubs:
return "clubs"
}
}
}
let hearts = Suit.Hearts
let heartsDescription = hearts.simpleDescription()
Beachte die zwei Arten, das Member Hearts oben zu referenzieren: Beim Zuweisen an die Konstante hearts wird das Enum-Member Suit.Hearts mit seinem vollständigen Namen referenziert, weil die Konstante keinen expliziten Typ hat. Im switch wird das Enum über .Hearts referenziert, weil der Wert von self bereits bekannt ist. Du kannst die bequeme Methode verwenden, wann immer du kannst.
Verwende struct, um eine Struktur zu erstellen. Strukturen unterstützen viele der gleichen Verhaltensweisen wie Klassen, einschließlich Methoden und Konstruktoren. Ein wichtiger Unterschied besteht darin, dass Strukturen immer kopiert werden, wenn sie im Code weitergegeben werden (Wertübergabe), während Klassen per Referenz übergeben werden.
struct Card {
var rank: Rank
var suit: Suit
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \
(suit.simpleDescription())"
}
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
<p><strong>注意:</strong>
Ein Instanzmitglied eines Enums kann Instanzwerte haben. Instanzen desselben Enum-Mitglieds können unterschiedliche Werte haben. Sie weisen die Werte beim Erstellen der Instanz zu. Der Unterschied zwischen zugewiesenen Werten und Rohwerten: Die Rohwerte eines Enums sind für alle seine Instanzen gleich, und Sie geben die Rohwerte beim Definieren des Enums an.
Zum Beispiel: Angenommen, eine Situation erfordert das Abrufen der Sonnenaufgangs- und Sonnenuntergangszeiten von einem Server. Der Server kann mit derselben Information oder einer Fehlermeldung antworten.
enum ServerResponse {
case Result(String, String)
case Error(String)
}
let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")
switch success {
case let .Result(sunrise, sunset):
let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
let serverResponse = "Failure... \(error)"
}
Hinweis:Die Sonnenaufgangs- und Sonnenuntergangszeiten werden tatsächlich aus einer teilweisen Übereinstimmung von ServerResponse ausgewählt.
Referenzen
Apple Swift-Programmiersprache – Erste-Schritte-Tutorial:http://gashero.iteye.com/blog/2075324
Die Swift-Programmiersprache:https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/