Event Bus und das Observer-Pattern in Swift (iOS/macOS)
Bei der Entwicklung für iOS und macOS besteht häufig die Notwendigkeit, eine lose gekoppelte Interaktion zwischen den Komponenten einer App zu organisieren. Das Observer-Pattern und seine Implementierung über eine Ereignisbus (Event Bus) sind ein leistungsstarkes Werkzeug zur Erstellung einer flexiblen und skalierbaren Architektur. In diesem Artikel werden wir untersuchen, wie man einen Event Bus in Swift implementiert, ihn mit den integrierten Mechanismen von Apple (NotificationCenter, Combine) vergleicht und praktische Beispiele für iOS/macOS zeigen.
Was sind Event Bus und das Observer-Pattern?
Das Observer-Pattern definiert eine "Eins-zu-Viele"-Beziehung zwischen Objekten: Wenn ein Objekt (der Herausgeber) seinen Zustand ändert, werden alle abhängigen Objekte (die Abonnenten) automatisch benachrichtigt.
Event Bus (Ereignisbus) ist ein zentralisierter Mechanismus, der es jeder Komponente ermöglicht, Ereignisse zu senden (zu veröffentlichen) und zu empfangen (zu abonnieren), ohne direkt voneinander zu wissen. Dies implementiert das Prinzip der losen Kopplung (loose coupling).
Im Apple-Ökosystem gibt es bereits integrierte Lösungen:
- NotificationCenter – eine klassische Implementierung des Observer-Patterns, jedoch mit Einschränkungen bei der Typisierung und Sicherheit.
- Combine – ein modernes Framework für reaktive Programmierung, das leistungsstarke Publisher/Subscriber bereitstellt.
Ein selbstgeschriebener Event Bus bietet jedoch die volle Kontrolle und kann leichter in Legacy-Projekte integriert werden.
Implementierung eines einfachen Event Bus in Swift
Wir erstellen einen universellen Ereignisbus unter Verwendung von Generics und schwachen Referenzen, um Speicherlecks zu vermeiden.
import Foundation
// Protokoll für Ereignisseprotocol Event { }
// Implementierung des Event Busfinal class EventBus { static let shared = EventBus() private var observers = [ObjectIdentifier: [WeakObserver]]() private init() { } // Abonnieren eines Ereignisses func subscribe<T: Event>(observer: AnyObject, handler: @escaping (T) -> Void) { let id = ObjectIdentifier(T.self) let weakRef = WeakObserver(observer: observer, handler: { event in guard let castedEvent = event as? T else { return } handler(castedEvent) }) if observers[id] != nil { observers[id]?.append(weakRef) } else { observers[id] = [weakRef] } // Bereinigung toter Referenzen cleanDeadObservers(for: id) } // Veröffentlichen eines Ereignisses func publish<T: Event>(_ event: T) { let id = ObjectIdentifier(T.self) guard let observerList = observers[id] else { return } for weakObserver in observerList { weakObserver.handler?(event) } // Entfernen freigegebener Objekte cleanDeadObservers(for: id) } // Abmelden func unsubscribe(observer: AnyObject, eventType: Event.Type) { let id = ObjectIdentifier(eventType) observers[id]?.removeAll { $0.observer === observer } } private func cleanDeadObservers(for id: ObjectIdentifier) { observers[id]?.removeAll { $0.observer == nil } }}
// Hilfsklasse für schwache Referenzprivate class WeakObserver { weak var observer: AnyObject? var handler: ((Event) -> Void)? init(observer: AnyObject, handler: @escaping (Event) -> Void) { self.observer = observer self.handler = handler }}Anwendungsbeispiel
Wir definieren benutzerdefinierte Ereignisse:
struct UserLoggedInEvent: Event { let userId: String let userName: String}
struct DataLoadedEvent: Event { let data: [String]}Verwendung in einem ViewController:
class ProfileViewController: UIViewController { override func Auch in der Bibliothek
Swagger/OpenAPI-Generierung für JavaScript (Node.js) — Werkzeuge und Bibliotheken
Java-Aufgabenplaner: Quartz Scheduler – Vollständiger Leitfaden
GraphQL-Bibliothek für Go (Golang) – vollständiger Leitfaden
Überwachung von Java-Anwendungen: Tools und Bibliotheken zur Metrikenerfassung