Zum Inhalt springen
Swift Anfรคnger 35 min

Structs, Classes & Enums

Die drei Wege, in Swift eigene Typen zu definieren. Value vs. Reference Semantics, Methoden, Mutating und Enums mit Assoziierten Werten.

Aktualisiert:
Inhaltsverzeichnis

Structs, Classes & Enums

In Swift hast du drei Optionen, eigene Typen zu bauen. Welchen du waehlst, macht einen grossen Unterschied.

Structs - Value Types

Structs sind in Swift die bevorzugte Wahl fuer Datenmodelle:

struct Person {
    let name: String
    var alter: Int
}

let anna = Person(name: "Anna", alter: 28)

Swift generiert automatisch einen Memberwise Initializer - du musst keinen Konstruktor schreiben.

Value Semantics

var a = Person(name: "Anna", alter: 28)
var b = a              // KOPIE
b.alter = 29

print(a.alter)         // 28 - a ist unveraendert
print(b.alter)         // 29

Zuweisungen machen eine Kopie. Das verhindert versehentliche Seiteneffekte.

Methoden in Structs

struct Rechteck {
    var breite: Double
    var hoehe: Double

    func flaeche() -> Double {
        breite * hoehe
    }

    mutating func skalieren(um faktor: Double) {
        breite *= faktor
        hoehe *= faktor
    }
}

var r = Rechteck(breite: 3, hoehe: 4)
print(r.flaeche())     // 12
r.skalieren(um: 2)
print(r.flaeche())     // 48

Wichtig: Methoden, die die Struct aendern, brauchen mutating. Und die Instanz muss var sein - nicht let.

Computed Properties

Properties, die einen Wert berechnen:

struct Rechteck {
    var breite: Double
    var hoehe: Double

    var flaeche: Double {
        breite * hoehe
    }

    var umfang: Double {
        2 * (breite + hoehe)
    }
}

let r = Rechteck(breite: 3, hoehe: 4)
print(r.flaeche)       // 12 - wie ein Property, nicht wie eine Methode

Classes - Reference Types

Classes sehen aehnlich aus, verhalten sich aber anders:

class Konto {
    var saldo: Double

    init(startSaldo: Double) {
        self.saldo = startSaldo
    }

    func einzahlen(_ betrag: Double) {
        saldo += betrag
    }
}

let a = Konto(startSaldo: 100)
let b = a               // KEINE Kopie - b zeigt aufs gleiche Objekt
b.einzahlen(50)

print(a.saldo)          // 150 - auch a sieht die Aenderung

Classes brauchen init

Keine automatischen Memberwise Initializer - du musst einen Konstruktor schreiben.

Keine mutating-Methoden

Classes koennen Felder immer aendern - kein mutating noetig.

Struct oder Class?

Faustregeln aus der Swift-Praxis:

  • Struct ist die Default-Wahl - fuer Datenmodelle, DTOs, Einstellungen, Werte
  • Class wenn du:
    • Vererbung brauchst (Classes erben, Structs nicht)
    • Identity wichtig ist (selbes Objekt, nicht gleicher Wert)
    • mit objektorientierten APIs (z.B. UIKit) arbeitest

In SwiftUI-Code siehst du fast nur Structs - das ist kein Zufall.

Vererbung (nur bei Classes)

class Tier {
    let name: String
    init(name: String) { self.name = name }

    func geraeusch() -> String {
        "..."
    }
}

class Hund: Tier {
    override func geraeusch() -> String {
        "Wuff!"
    }
}

class Katze: Tier {
    override func geraeusch() -> String {
        "Miau!"
    }
}

let tiere: [Tier] = [Hund(name: "Bello"), Katze(name: "Lilli")]
for tier in tiere {
    print("\(tier.name): \(tier.geraeusch())")
}
  • class Hund: Tier - Hund erbt von Tier
  • override ist Pflicht, wenn du eine Methode ueberschreibst

final - Vererbung verbieten

final class Konfiguration { /* ... */ }

Enums

Enums in Swift sind sehr maechtig - weit mehr als nur Konstanten.

Einfach

enum Richtung {
    case nord
    case sued
    case ost
    case west
}

var r = Richtung.nord
r = .west              // Kurzschreibweise, wenn Typ klar ist

Enums mit Raw Values

enum Statuscode: Int {
    case ok = 200
    case notFound = 404
    case serverError = 500
}

let s = Statuscode.notFound
print(s.rawValue)       // 404

let vom404 = Statuscode(rawValue: 404) // Optional<Statuscode>

Enums mit Associated Values (Superkraft!)

enum Event {
    case klick(x: Double, y: Double)
    case tastendruck(Character)
    case schliessen
}

let e = Event.klick(x: 12.5, y: 48.3)

switch e {
case .klick(let x, let y):
    print("Klick bei (\(x), \(y))")
case .tastendruck(let c):
    print("Taste: \(c)")
case .schliessen:
    print("Schliessen")
}

Jeder Case kann eigene Daten tragen - wie Sum Types in Rust oder Haskell.

Enums mit Methoden

enum Richtung {
    case nord, sued, ost, west

    var gegenueber: Richtung {
        switch self {
        case .nord: return .sued
        case .sued: return .nord
        case .ost: return .west
        case .west: return .ost
        }
    }
}

print(Richtung.nord.gegenueber) // sued

Optional unter der Haube

Der bekannte Optional-Typ ist ein Enum:

enum Optional<Wrapped> {
    case none
    case some(Wrapped)
}

Jetzt verstehst du, warum du .some(5) oder .none schreiben kannst.

struct vs class vs enum - Kurzuebersicht

FeatureStructClassEnum
Value Semanticsโœ“โœ—โœ“
Reference Semanticsโœ—โœ“โœ—
Vererbungโœ—โœ“โœ—
Methodenโœ“โœ“โœ“
Computed Propertiesโœ“โœ“โœ“
Stored Propertiesโœ“โœ“โœ—
Associated Valuesโœ—โœ—โœ“

Protocols - kurzer Vorblick

Protokolle beschreiben Faehigkeiten, die Typen implementieren koennen:

protocol Fliegend {
    func fliegen()
}

struct Vogel: Fliegend {
    func fliegen() {
        print("Flatter flatter")
    }
}

Das ist Swifts Protocol-Oriented Programming, das wir in einem spaeteren Kapitel vertiefen.

Praktisches Beispiel

struct Produkt {
    let name: String
    let preis: Double
}

struct Warenkorb {
    private(set) var artikel: [Produkt] = []

    var gesamt: Double {
        artikel.reduce(0) { $0 + $1.preis }
    }

    mutating func hinzufuegen(_ p: Produkt) {
        artikel.append(p)
    }
}

var korb = Warenkorb()
korb.hinzufuegen(Produkt(name: "Buch", preis: 19.99))
korb.hinzufuegen(Produkt(name: "Kaffee", preis: 3.50))
print("Gesamt: \(korb.gesamt)") // 23.49

private(set) erlaubt Lesen von aussen, Schreiben nur von innen.

Zusammenfassung

  • Structs sind die bevorzugte Wahl - Value Semantics, automatischer Init
  • Classes haben Reference Semantics und Vererbung
  • Enums sind in Swift Sum Types mit Associated Values
  • mutating bei Methoden, die Structs/Enums aendern
  • Computed Properties fuer abgeleitete Werte

Damit hast du das Fundament, um eigene Swift-Typen zu modellieren. Im weiteren Kurs gehen wir auf Protocols, generische Programmierung, Fehlerbehandlung und async/await ein.

Zurรผck zum Swift Kurs