Singleton — was ist das, eine einzelne Klasseninstanz in iOS und Android

Autor: IT Sectr Veröffentlicht: 2026-02-17 Lesezeit: 8 Min.

Singleton — ein Erzeugungsmuster, das eine einzelne Klasseninstanz garantiert und einen globalen Zugriffspunkt darauf bereitstellt. Singleton wird in der mobilen Entwicklung häufig für gemeinsame Ressourcen verwendet: Netzwerkclients, Datenbanken, Einstellungsmanager. Das Muster wird im klassischen GoF-Buch (1994) beschrieben und ist eines der bekanntesten. Mehr erfahren Sie auf Refactoring Guru: Singleton.

Wichtige Punkte

  • Singleton — garantiert eine Klasseninstanz pro Anwendung
  • Globaler Zugriffspunkt — statische Eigenschaft shared oder companion object
  • Thread safety — Synchronisation für korrekte Arbeit in Multithread-Umgebungen erforderlich
  • Kritik — Singleton erschwert Tests und erzeugt versteckte Abhängigkeiten
  • Alternativen — Dependency Injection, Service Locator zum Ersetzen von Singleton

Was ist Singleton: das Wesen des Singleton-Musters?

Singleton — ein von GoF (Gang of Four) 1994 beschriebenes Erzeugungsmuster. Das Muster löst zwei Probleme: Es beschränkt die Instanziierung einer Klasse auf ein einzelnes Objekt und bietet globalen Zugriff auf dieses Objekt. Singleton ist nützlich für Ressourcen, die eindeutig sein müssen: Session-Fabriken, Bild-Caches, Datenbankverbindungsmanager, Crashlytics- oder Analytics-Clients.

Singleton-Implementierung erfordert einen privaten Konstruktor (verhindert externe Erstellung), ein statisches Feld mit der einzigen Instanz und eine statische Zugriffsmethode (shared, instance, getInstance). Clients rufen Singleton.shared.method() auf, ohne sich um die Objekterstellung zu kümmern. Das Muster ist in iOS und Android beliebt: URLSession.shared, UserDefaults.standard, FirebaseApp.sharedInstance — alles Singletons. Übermäßiger Gebrauch von Singleton führt jedoch zum Anti-Pattern Global State.

Singleton-Probleme — versteckte Abhängigkeiten (Klassen hängen implizit vom Singleton-Objekt ab), Testkomplexität (Instanz kann in Tests nicht ohne zusätzlichen Aufwand ersetzt werden), Verletzung des Single Responsibility Principle (Singleton verwaltet sowohl seine Instanz als auch die Geschäftslogik). Moderne mobile Entwicklung bevorzugt DI (Dagger, Hilt, Swinject) zur Verwaltung einzelner Instanzen — der DI-Container erstellt das Objekt einmal und injiziert es über den Konstruktor.

Singleton in iOS mit Swift: shared und statische Eigenschaften

Swift Singleton wird über eine statische shared-Eigenschaft mit einem privaten Initialisierer implementiert. Seit Swift 3 ist die träge Initialisierung statischer Eigenschaften garantiert threadsicher — der Compiler fügt automatisch Synchronisation über dispatch_once hinzu. Es reicht aus, static let shared = Class() zu deklarieren und init() privat zu machen. Swift benötigt keine zusätzliche Synchronisation für Single-Thread-Zugriff nach der Initialisierung.

swift
final class NetworkManager {
    // Thread-sicheres Singleton
    static let shared = NetworkManager()

    private init() {
        URLSessionConfiguration.default.timeoutIntervalForRequest = 30
    }

    private var cache = NSCache<NSString, NSData>()

    func fetchData(from url: URL) async throws -> Data {
        let key = url.absoluteString as NSString
        if let cached = cache.object(forKey: key) {
            return cached as Data
        }
        let (data, _) = try await URLSession.shared.data(from: url)
        cache.setObject(data as NSData, forKey: key)
        return data
    }
}

// Verwendung
let data = try await NetworkManager.shared.fetchData(from: url)

Apple Singleton — viele iOS SDK-Objekte verwenden Singleton: UIApplication.shared, UIScreen.main, FileManager.default, NotificationCenter.default, UserDefaults.standard. Apple verwendet Singleton für physisch eindeutige Dienste (ein Bildschirm, eine Anwendung). Entwickler kopieren dieses Muster für ihre eigenen Dienste. In SwiftUI wird der globale Zugriff auf Singleton durch Environment und @EnvironmentObject ersetzt, was die Testbarkeit verbessert.

Singleton in Android mit Kotlin: companion object und object

Kotlin Singleton — der einfachste Weg: Das Schlüsselwort object deklariert eine Singleton-Klasse mit träger Initialisierung beim ersten Zugriff. Kotlin object ist threadsicher und benötigt keine zusätzliche Synchronisation. Wenn ein Singleton mit Konstruktorparametern benötigt wird, wird ein companion object mit einem lazy-Delegaten verwendet. In Android ist Singleton oft für den Application-Kontext und Dienste erforderlich, die über Application.onCreate() initialisiert werden.

kotlin
// Option 1: object — einfaches Singleton ohne Parameter
object AppPreferences {
    private val prefs = Application.instance
        .getSharedPreferences("app", Context.MODE_PRIVATE)

    var isFirstLaunch: Boolean
        get() = prefs.getBoolean("first_launch", true)
        set(value) = prefs.edit { putBoolean("first_launch", value) }
}

// Option 2: companion object — Singleton mit Parametern
class ApiClient private constructor(baseUrl: String) {
    companion object {
        @Volatile
        private var instance: ApiClient? = null

        fun getInstance(baseUrl: String): ApiClient {
            return instance ?: this.synchronized {
                instance ?: ApiClient(baseUrl).also { instance = it }
            }
        }
    }

    fun request(endpoint: String): String { /* ... */ }
}

Android SDK Singleton — viele Android-Systemdienste implementieren Singleton: context.getSystemService(), Room.databaseBuilder(), Retrofit.Builder(). Beispiele umfassen SharedPreferences, MediaPlayer, AudioManager. In Android-Anwendungen wird Singleton oft für Repositories, Manager und Fabriken verwendet. Google empfiehlt, Singleton durch DI (Hilt, Koin) zu ersetzen, wobei der Singleton-Scope (Scope.Singleton oder @Singleton) vom Container verwaltet wird, während die Klasse testbar bleibt.

Thread safety: dispatch_once, synchronized und lock

Thread safety — eine kritische Anforderung für Singleton in Multithread-Umgebungen. Ohne Synchronisation können zwei Threads gleichzeitig instance == null prüfen und zwei Instanzen erstellen. Die Lösung ist Sperren während der ersten Erstellung und Freigabe nach der Initialisierung. In Swift sind statische Eigenschaften (static let) standardmäßig threadsicher. In Kotlin ist object threadsicher. Für Java-Stil in Kotlin wird synchronized oder @Volatile + double-check locking verwendet.

SpracheMechanismusThreadsicherheitTräge Initialisierung
Swiftstatic letdispatch_once (automatisch)Ja, beim ersten Zugriff
Kotlin objectObject-DeklarationKlasseninitialisierer threadsicherJa, beim ersten Zugriff
Kotlin companionsynchronized + @VolatileDouble-checked lockingJa, über lazy oder synchronized
Javasynchronized + volatileDouble-checked lockingJa, in getInstance()

Double-checked locking — ein Muster für die träge Singleton-Initialisierung. Erste Prüfung ohne Synchronisation (schnell, wenn die Instanz bereits existiert), zweite innerhalb von synchronized (Erstellung nur durch einen Thread). @Volatile garantiert Sichtbarkeit von Änderungen für alle Threads. Ohne volatile könnte ein anderer Thread ein teilweise erstelltes Objekt sehen. In Kotlin implementiert der lazy-Delegat mit LazyThreadSafetyMode.SYNCHRONIZED automatisch double-checked locking.

Singleton vs Dependency Injection: wann verwenden

Dependency Injection — eine Alternative zu Singleton zur Verwaltung einzelner Instanzen. Ein DI-Container (Dagger, Hilt, Koin, Swinject) erstellt das Objekt einmal in einem Singleton-Scope und injiziert es über den Konstruktor. Die Klasse weiß nichts über ihren Singleton-Status — der Container entscheidet. Der Code wird testbar: Das DI-Modul wird in Tests durch ein Mock-Modul ersetzt. DI-Vorteile: explizite Abhängigkeiten im Konstruktor, Überschreibbarkeit, einheitlicher Lebenszyklus.

Wann Singleton gerechtfertigt ist — Objekte auf Systemebene: Crashlytics, Analytics, Logging. Diese Dienste werden einmal in AppDelegate/Application initialisiert und überall verwendet. DI ist dafür übertrieben. Singleton ist auch praktisch für Bild-Caches (NSCache, Coil, Glide), wo globaler Zugriff durch Leistung gerechtfertigt ist. Für alles andere ist DI vorzuziehen: Es macht Abhängigkeiten sichtbar, vereinfacht Tests und Refactoring.

Hybrider Ansatz — Singleton mit Überschreibbarkeit für Tests. In Swift ein Protokoll + statische Eigenschaft, die Tests ersetzen können (z.B. über URLProtocol für URLSession). In Kotlin eine offene Klasse mit einer injizierbaren Eigenschaft, wo Tests über Reflexion oder einen Setter einen Mock setzen. Dieser Ansatz bewahrt die Einfachheit von Singleton, bietet aber Testmöglichkeiten. Google empfiehlt Hilt für Android, Apple erzwingt DI nicht für iOS — die Wahl hängt vom Team ab.

Häufig gestellte Fragen

Ist Singleton ein Anti-Pattern?

Nein, Singleton ist ein GoF-Muster, aber seine häufige falsche Verwendung verwandelt es in das Global State Anti-Pattern. Singleton ist für physisch eindeutige Ressourcen (Bildschirm, Drucker, Dateisystem) gerechtfertigt. Probleme entstehen, wenn Singleton zur Datenverwaltung verwendet wird: versteckte Abhängigkeiten, Testkomplexität, Verletzung des Single Responsibility Principle. Eine moderne Alternative ist DI mit Singleton-Scope.

Wie testet man Code, der Singleton verwendet?

Drei Ansätze: (1) über Protokoll — Singleton implementiert ein Protokoll, Tests tauschen die Implementierung aus; (2) über DI — Singleton wird als Abhängigkeit über den Konstruktor injiziert; (3) über reset-Methode — Singleton hat eine Methode zum Zurücksetzen des Zustands in Tests (nur für Test-Builds). Der erste Ansatz ist vorzuziehen, der dritte ist gefährlich für die Produktion. Swift erlaubt das Ersetzen der shared-Eigenschaft durch Laufzeitmanipulation in Tests.

Wie unterscheidet sich Kotlin object von Java Singleton?

Kotlin object ist ein Sprachkonstrukt, das Singleton auf Bytecode-Ebene erstellt. Im Gegensatz zur Java-Implementierung mit privatem Konstruktor und getInstance() garantiert object Threadsicherheit, träge Initialisierung und verbietet Vererbung. Java Singleton erfordert manuelle Synchronisation (synchronized) und volatile für korrekte Arbeit in Multithread-Umgebungen. Kotlin object ist der sicherste und prägnanteste Weg in Android.

Kann Singleton vererbt werden?

Vererbung von Singleton verletzt das Muster: Wenn eine Singleton-Klasse vererbt werden kann, könnte eine Unterklasse eine zweite Instanz erstellen und die Eindeutigkeit verletzen. In Swift verbietet final class die Vererbung. Kotlin object kann nicht vererbt werden (object ist sealed). Wenn ein Singleton mit Variabilität benötigt wird, verwenden Sie einen DI-Container mit Singleton-Scope: Er garantiert eine einzelne Instanz und unterstützt Vererbung über Schnittstellen.

Wie übergibt man Parameter an ein Singleton in Android?

Parameter werden über init(context: Application) oder getInstance(param) übergeben. Kotlin object akzeptiert keine Parameter — verwenden Sie ein companion object mit einer Factory-Methode getInstance(param). Hilt löst das Problem: @Singleton + @Inject constructor(context: Application) — der DI-Container injiziert den Application-Kontext automatisch. Für einen Retrofit-Client werden Parameter (baseUrl, interceptors) über einen Builder im DI-Modul übergeben.

Zusammenfassung

  • Singleton — Muster mit einer einzigen Instanz und globalem Zugriff
  • Swift shared — static let mit Compiler-garantierter Threadsicherheit
  • Kotlin object — träge Initialisierung ohne zusätzlichen Code
  • Thread safety — double-checked locking für Java, automatisch für Swift/Kotlin
  • Apple SDK — UIApplication.shared, UserDefaults.standard, FileManager.default
  • Android SDK — Retrofit, Room, SharedPreferences über Singleton-Manager
  • Alternativen — Dependency Injection für testbaren Code

Wir entwickeln eine mobile Applikation schlüsselfertig

IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.

Projekt besprechen

Lesen Sie auch