EMZETT.
Login

Kurz: Ein Protokoll beschreibt Anforderungen (wie ein Interface). Structs, Klassen und Enums können es erfüllen. Mit Protokoll-Erweiterungen gibst du Standardimplementierungen.

Teil des Kurses Swift

Kapitel 5 von 8 im Kurs Swift. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Protokolle

Ein Protokoll beschreibt Anforderungen (wie ein Interface). Structs, Klassen und Enums können es erfüllen. Mit Protokoll-Erweiterungen gibst du Standardimplementierungen.

protocol Beschreibbar {
    var name: String { get }
    func beschreibe() -> String
}
 
extension Beschreibbar {
    func beschreibe() -> String { "Ich bin \(name)" }            // Standardimplementierung
    func lautBeschreibe() -> String { beschreibe().uppercased() }
}
 
struct Hund: Beschreibbar { let name: String }
struct Auto: Beschreibbar {
    let name: String
    let ps: Int
    func beschreibe() -> String { "\(name) mit \(ps) PS" }
}
 
let dinge: [any Beschreibbar] = [Hund(name: "Rex"), Auto(name: "Golf", ps: 110)]
for d in dinge { print(d.beschreibe(), "|", d.lautBeschreibe()) }
 
// Wichtige Standardprotokolle: Equatable, Comparable, Hashable, Codable, CustomStringConvertible
struct Version: Comparable, CustomStringConvertible {
    let major: Int, minor: Int
    static func < (a: Version, b: Version) -> Bool { (a.major, a.minor) < (b.major, b.minor) }
    var description: String { "v\(major).\(minor)" }
}
let versionen = [Version(major: 1, minor: 4), Version(major: 1, minor: 0), Version(major: 2, minor: 1)]
print(versionen.sorted(), versionen.max()!, Version(major: 1, minor: 0) < Version(major: 1, minor: 1))

Ausgabe:

Ich bin Rex | ICH BIN REX
Golf mit 110 PS | GOLF MIT 110 PS
[v1.0, v1.4, v2.1] v2.1 true

Extensions

Mit extension ergänzt du bestehende Typen, auch solche aus der Standardbibliothek:

extension Int {
    var istGerade: Bool { self % 2 == 0 }
    func hoch(_ n: Int) -> Int { n == 0 ? 1 : self * hoch(n - 1) }
}
extension String {
    var umgekehrt: String { String(self.reversed()) }
    func wiederholt(_ n: Int) -> String { String(repeating: self, count: n) }
}
print(4.istGerade, 7.istGerade, 2.hoch(10), "Swift".umgekehrt, "ab".wiederholt(3))

Ausgabe:

true false 1024 tfiwS ababab

Generics

func tausche<T>(_ a: inout T, _ b: inout T) { let t = a; a = b; b = t }
 
func groesstes<T: Comparable>(_ liste: [T]) -> T? {
    guard var m = liste.first else { return nil }
    for x in liste where x > m { m = x }
    return m
}
 
struct Stapel<Element> {
    private var daten: [Element] = []
    mutating func push(_ x: Element) { daten.append(x) }
    mutating func pop() -> Element? { daten.popLast() }
    var oberstes: Element? { daten.last }
    var groesse: Int { daten.count }
}
 
var s = "a", t = "b"
tausche(&s, &t)
print(s, t, groesstes([3, 9, 4]) as Any, groesstes(["kiwi", "apfel"]) as Any, groesstes([Int]()) as Any)
 
var stapel = Stapel<Int>()
stapel.push(1); stapel.push(2); stapel.push(3)
print(stapel.pop() as Any, stapel.oberstes as Any, stapel.groesse)

Ausgabe:

b a Optional(9) Optional("kiwi") nil
Optional(3) Optional(2) 2

Fehlerbehandlung

Fehlerfälle sind Typen, die Error erfüllen. Fehlbare Funktionen markierst du mit throws, Aufrufe mit try:

enum RechenFehler: Error {
    case durchNull
    case negativ(wert: Double)
}
 
func teile(_ a: Int, _ b: Int) throws -> Int {
    guard b != 0 else { throw RechenFehler.durchNull }
    return a / b
}
 
func wurzel(_ x: Double) throws -> Double {
    if x < 0 { throw RechenFehler.negativ(wert: x) }
    return x.squareRoot()
}
 
do {
    print(try teile(10, 2))
    print(try teile(1, 0))
} catch RechenFehler.durchNull {
    print("durch null!")
} catch {
    print("anderer Fehler: \(error)")
}
 
do { _ = try wurzel(-4) } catch RechenFehler.negativ(let w) { print("negativ:", w) }
 
let ok = try? teile(9, 3)              // try?: Optional statt Fehler
let schlecht = try? teile(9, 0)
print(ok as Any, schlecht as Any)
 
func lies(_ text: String) throws -> Int {
    guard let n = Int(text) else { throw NSError(domain: "Eingabe", code: 1) }
    return n
}
import Foundation
for t in ["42", "x"] {
    do { print(try lies(t)) } catch { print("Fehler bei \(t)") }
}
 
func aufraeumen() {
    defer { print("defer: aufgeräumt") }       // läuft beim Verlassen des Blocks
    print("arbeite")
}
aufraeumen()
 
enum Ergebnis2<T> { case ok(T), fehler(Error) }
let r: Result<Int, RechenFehler> = .success(5)
let r2: Result<Int, RechenFehler> = .failure(.durchNull)
for res in [r, r2] {
    switch res {
    case .success(let v): print("Erfolg", v)
    case .failure(let e): print("Fehler", e)
    }
}

Ausgabe:

5
durch null!
negativ: -4.0
Optional(3) nil
42
Fehler bei x
arbeite
defer: aufgeräumt
Erfolg 5
Fehler durchNull

Merke

  • Protokolle beschreiben Anforderungen, Protokoll-Extensions liefern Standardverhalten
  • extension erweitert bestehende Typen
  • Generics <T: Comparable> für wiederverwendbaren Code
  • Fehler: enum ... : Error, throws, try/try?/try!, do-catch, defer, Result
  • Standardprotokolle: Equatable, Comparable, Hashable, Codable

Übungsaufgabe

Schreibe ein Protokoll Flaeche mit flaeche() -> Double und zwei Typen, die es erfüllen.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Structs, Klassen und Enums

Weiter: Nebenläufigkeit, SwiftUI und Ökosystem

Alle Kapitel: Swift im Überblick