Strukturen und Klassen sind zwei der wichtigsten Bausteine der Programmierung in Swift. Mit beiden bündelst du zusammengehörige Daten und Funktionen zu in sich geschlossenen Einheiten. Und wenn du eine Struktur oder eine Klasse definierst, definierst du einen neuen Typ, den du in Swift verwenden kannst, genau wie die Typen, mit denen du schon gearbeitet hast, etwa Int und String.
Strukturen und Klassen haben in Swift viele Gemeinsamkeiten. Unter anderem können beide Werte in Eigenschaften speichern und über Methoden Funktionalität bereitstellen.
Jede von ihnen bietet noch zusätzliche Funktionalität, die in dieser Übung keine Rolle spielt.
Strukturen und Klassen werden ungefähr gleich definiert. Sie beginnen mit dem passenden Schlüsselwort, gefolgt vom Namen des Typs, der definiert wird. Danach folgt der Body der Struktur bzw. der Klasse, eingeschlossen von geschweiften Klammern.
Der Body kann gespeicherte Eigenschaften enthalten, die genauso definiert werden und sich genauso verhalten wie normale Konstanten oder Variablen.
struct CharacterStats {
var health = 0.0
var speed = 0
var strength = 0
}
class GameCharacter {
var stats = CharacterStats()
var characterClass: String?
var name: String?
var active = false
let id = makeRandomID()
}
Wie oben erwähnt, definierst du beim Definieren einer Struktur oder Klasse nur einen neuen Typ. Das ist nur der Bauplan dafür, wie die Werte dieses Typs aussehen werden. Es werden aber noch keine Werte dieses Typs erzeugt, mit denen du arbeiten kannst.
Um eine Instanz dieses Typs zu erzeugen, schreibst du den Namen des Typs gefolgt von einem Paar runder Klammern.
let someStats = CharacterStats()
let someCharacter = GameCharacter()
Dadurch werden Werte dieser Typen erzeugt, wobei die Eigenschaften mit den Standardwerten aus der Definition befüllt werden. Beachte, dass in optionalen Fällen wie der Eigenschaft name von GameCharacter die Eigenschaft standardmäßig nil ist, wenn kein Wert angegeben wird, genau wie beim Definieren gewöhnlicher optionaler Typen, bei denen nicht sofort ein Wert angegeben wird.
Bei Strukturen stellt Swift automatisch etwas bereit, das memberwise Initializer heißt. Darin kannst du in den Klammern Werte für die Eigenschaften der Struktur angeben, die die Standardwerte aus der Definition überschreiben.
let differentStats = CharacterStats(health: 100.0, speed: 6, strength: 18)
Auf Eigenschaften von Strukturen und Klassen greifst du mit der Punktnotation zu: Auf den Namen des Werts folgt ein Punkt (.) und dann der Name der Eigenschaft. Hat eine Eigenschaft einer Struktur oder Klasse selbst Eigenschaften, kannst du mit dieser Punktnotation auch auf diese verschachtelten Eigenschaften zugreifen.
Mit dieser Notation kannst du den Wert der Eigenschaft sowohl abrufen als auch, wo erlaubt, ändern.
someStats.health
// => 0
someCharacter.name
// => nil
someStats.health = 87.3
someStats.health
// => 87.3
someCharacter.name = "Luther"
someCharacter.name
// => "Luther"
someCharacter.id = "new id"
// Error: Cannot assign to property: 'id' is a 'let' constant
Wie Eigenschaften, die Daten in deinen Strukturen und Klassen speichern, kannst du auch Methoden definieren, die Funktionen in deiner Struktur oder Klasse speichern.
Methoden werden genauso definiert wie eine normale Funktion, nur innerhalb des Bodys der Struktur oder Klasse. Beachte, dass eine Funktion mit dem Schlüsselwort mutating versehen werden muss, wenn sie den Wert einer Eigenschaft in einer Struktur ändert. Außerdem muss eine Eigenschaft mit var statt let definiert werden, wenn eine Methode sie ändern können soll, genau wie bei normalen Variablen.
struct CharacterStats {
var health = 0.0
var speed = 0
var strength = 0
mutating func takeHit(_ damage: Double) {
health = max(0.0, health - damage)
}
func canLift(_ weight: Int) -> Bool {
weight < strength * 100
}
}
class GameCharacter {
var stats = CharacterStats()
var characterClass: String?
var name: String?
var active = false
let id: String = makeRandomID()
func takesDamage(_ damage: Double) {
stats.takeHit(damage)
if stats.health <= 0 {
active = false
}
}
func sayName() -> String {
return "My name is \(name ?? "no one"), my class is \(characterClass ?? "undetermined")"
}
func lift(_ weight: Int) -> String {
if stats.canLift(weight) {
return "No problem!"
} else {
return "Ooof! No way."
}
}
}
Diese Methoden rufst du mit der Punktnotation auf, genau wie Eigenschaften.
var myChar = GameCharacter()
myChar.stats = CharacterStats(health: 72.8, speed: 19, strength: 6)
myChar.active = true
myChar.lift(750)
// => "Ooof! No way."
myChar.takesDamage(80)
myChar.active
// => false
Jede Instanz einer Struktur oder Klasse hat eine implizite Eigenschaft namens self, die auf die Instanz selbst verweist.
Am häufigsten wird self verwendet, um zwischen einer Eigenschaft der Instanz und einem Parameter mit demselben Namen zu unterscheiden:
struct Position {
var x = 0
mutating func move(x: Int) {
// Here, 'self.x' refers to the struct's property,
// while 'x' refers to the parameter passed to the method.
self.x = x
}
}
Swift-Typen bieten außerdem das großgeschriebene Schlüsselwort Self, das auf den Typ der Instanz verweist und nicht auf die Instanz selbst.
In dieser Übung simulierst du ein fensterbasiertes Computersystem. Du erstellst Fenster, die verschoben und in ihrer Größe geändert werden können und ihren Inhalt anzeigen. Das folgende Bild zeigt die Werte, mit denen du im Folgenden arbeiten wirst.
<--------------------- screenSize.width --------------------->
^ ╔════════════════════════════════════════════════════════════╗
| ║ ║
| ║ position.x,_ ║
| ║ position.y \ ║
| ║ \<----- size.width -----> ║
| ║ ^ *──────────────────────┐ ║
| ║ | │ title │ ║
| ║ | ├──────────────────────┤ ║
screenSize.height ║ | │ │ ║
| ║ size.height │ │ ║
| ║ | │ contents │ ║
| ║ | │ │ ║
| ║ | │ │ ║
| ║ v └──────────────────────┘ ║
| ║ ║
| ║ ║
v ╚════════════════════════════════════════════════════════════╝
Definiere eine Struktur namens Size mit zwei Int-Eigenschaften, width und height, die die aktuelle Breite bzw. Höhe des Fensters speichern.
Die anfängliche Breite und Höhe sollten 80 bzw. 60 betragen.
Füge eine Methode resize(newWidth:newHeight:) hinzu, die neue Parameter für Breite und Höhe entgegennimmt und die Eigenschaften an die neue Größe anpasst.
let size1080x764 = Size(width: 1080, height: 764)
// Returns Size
var size1200x800 = size1080x764
// Returns Size
size1200x800.resize(newWidth: 1200, newHeight: 800)
size1200x800.height
// Returns 800
Definiere eine Struktur namens Position mit zwei Int-Eigenschaften, x und y, die die aktuelle horizontale bzw. vertikale Position der linken oberen Ecke des Fensters speichern.
Die Anfangswerte von x und y sollten jeweils 0 sein. Die Position (0, 0) ist die linke obere Ecke des Bildschirms, wobei die x-Werte größer werden, wenn du dich nach rechts bewegst, und die y-Werte größer werden, wenn du dich nach unten bewegst.
Füge eine Methode moveTo(newX:newY:) hinzu, die neue x- und y-Parameter entgegennimmt und die Eigenschaften an die neue Position anpasst.
var point = Position(x: 10, y: 20)
// Returns Position
point.moveTo(newX: 100, newY: -100)
point.y
// Returns -100
Definiere eine Klasse Window mit den folgenden Eigenschaften:
title : String, der Anfangswert ist „New Window“screenSize : Size, ein konstanter Wert mit width = 800 und height = 600size : Size, der Anfangswert ist der Standardwert der Size-Strukturposition : Position, der Anfangswert ist der Standardwert der Position-Strukturcontents : String?, der Anfangswert ist nil
Außerdem solltest du einen leeren Initialisierer für die Klasse definieren.
let window = Window()
window.title
// Returns "New Window"
resize(to:) : (Size) -> () - Diese Methode nimmt eine Size-Struktur als Eingabe und versucht, das Fenster auf die angegebene Größe zu ändern. Die neue Größe darf jedoch bestimmte Grenzen nicht überschreiten.x = 400, y = 300 befindet und eine Größenänderung auf height = 400, width = 300 angefordert wird, dann würde das Fenster auf height = 300, width = 300 geändert, da der Bildschirm in y-Richtung nicht groß genug ist, um die Anforderung vollständig unterzubringen.move(to:) : (Position) -> () - Das ähnelt resize(to:), allerdings passt diese Methode die Position des Fensters an den angeforderten Wert an, nicht die Größe. Wie bei resize darf die neue Position bestimmte Grenzen nicht überschreiten.x und y.x = 250, y = 100 befindet und eine Verschiebung auf x = 600, y = 200 angefordert wird, dann würde das Fenster auf x = 550, y = 200 verschoben, da der Bildschirm in x-Richtung nicht groß genug ist, um die Anforderung vollständig unterzubringen.update(title:) : (String) -> () - Diese Methode setzt die Eigenschaft title auf den Wert des übergebenen Strings.update(text:) : (String?) -> () - Diese Methode setzt die Eigenschaft contents auf den Wert des übergebenen optionalen Strings.display() : () -> String - Diese Methode gibt einen String zurück, der den aktuellen Zustand des Fensters beschreibt. Wenn das Fenster zum Beispiel den title „My First Window“ mit Position: x = 10, y = 100; Größe: width = 200, height = 150; und contents: „I 😍 my window“ hat, sollte sie den String "My First Window\nPosition: (10, 100), Size: (200 x 150)\nI 😍 my window\n" zurückgeben - Ist contents nil, sollte die letzte Zeile "[This window intentionally left blank]" lautenDas Fenstersystem sollte einen Initialisierer haben, damit das Fenster mit eigenen Eingaben erstellt werden kann.
Erstelle einen weiteren Initialisierer für die Klasse Window.
Der Initialisierer sollte die folgenden Parameter entgegennehmen: title, contents und die beiden optionalen Parameter size und position.
Die Parameter size und position sollten standardmäßig die Standardwerte der Strukturen Size und Position annehmen.
let window = Window(title: "My First Window", contents: "I 😍 my window")
window.display()
// Returns "My First Window\nPosition: (0, 0), Size: (80 x 60)\nI 😍 my window\n"
let window2 = Window(title: "My Second Window", contents: "I 😍 my window", size: Size(width: 200, height: 150), position: Position(x: 10, y: 100))
window2.display()
// Returns "My Second Window\nPosition: (10, 100), Size: (200 x 150)\nI 😍 my window\n"
Melde dich bei Exercism an, um Swift mit 35 Konzepte116 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.