Singleton — mi ez, osztály egyetlen példánya iOS-ben és Androidban

Szerző: IT Sectr Megjelenés: 2026-02-17 Olvasási idő: 8 perc

Singleton — létrehozási minta, amely garantálja az osztály egyetlen példányát és globális hozzáférési pontot biztosít hozzá. Singleton széles körben használatos a mobilfejlesztésben megosztott erőforrásokhoz: hálózati kliensek, adatbázisok, beállításkezelők. A mintát a klasszikus GoF (1994) könyv írja le, és az egyik legismertebb maradt. Bővebben — a Refactoring Guru: Singleton oldalon.

Lényeg

  • Singleton — egy példányt garantál az osztályból a teljes alkalmazásban
  • Globális hozzáférési pont — statikus shared tulajdonság vagy companion object
  • Thread safety — szinkronizáció szükséges a helyes működéshez többszálú környezetben
  • Kritika — a Singleton megnehezíti a tesztelést és rejtett függőségeket hoz létre
  • Alternatívák — Dependency Injection, Service Locator a Singleton helyettesítésére

Mi az a Singleton: a singleton minta lényege?

Singleton — létrehozási minta, amelyet a GoF (Gang of Four) írt le 1994-ben. A minta két feladatot old meg: korlátozza az osztálypéldány létrehozását egy objektumra, és globális hozzáférést biztosít ehhez az objektumhoz. A Singleton hasznos olyan erőforrásokhoz, amelyeknek egyedinek kell lenniük: munkamenet-gyár, képgyorsítótár, adatbázis-kapcsolatkezelő, Crashlytics vagy Analytics kliens.

A Singleton megvalósítása privát konstruktort (külső létrehozás tiltása), statikus mezőt egyetlen példánnyal és statikus hozzáférési metódust (shared, instance, getInstance) igényel. A kliensek Singleton.shared.method() hívással érik el az objektumot anélkül, hogy annak létrehozásával kellene foglalkozniuk. A minta népszerű iOS-ben és Androidban: URLSession.shared, UserDefaults.standard, FirebaseApp.sharedInstance — mind Singleton. A Singleton túlzott használata azonban a Global State anti-mintához vezet.

A Singleton problémái — rejtett függőségek (az osztályok implicit módon függnek a Singleton objektumtól), tesztelés nehézsége (a példány nem cserélhető ki a tesztben további erőfeszítés nélkül), a Single Responsibility Principle megsértése (a Singleton kezeli saját példányát és az üzleti logikát is). A modern mobilfejlesztés a DI-t (Dagger, Hilt, Swinject) részesíti előnyben az egyetlen példányok kezelésére — a DI konténer egyszer hozza létre az objektumot, és konstruktoron keresztül injektálja.

Singleton iOS-ben Swift nyelven: shared és statikus tulajdonságok

Swift Singleton egy shared statikus tulajdonságon keresztül valósul meg privát inicializálóval. Swift 3 óta a statikus tulajdonságok lusta inicializálása garantáltan szálbiztos — a fordító automatikusan hozzáadja a szinkronizációt dispatch_once segítségével. Elég deklarálni a static let shared = Class() kifejezést, és az init()-t priváttá tenni. A Swift nem igényel további szinkronizációt az egyszálú hozzáféréshez az inicializálás után.

swift
final class NetworkManager {
    // Thread-safe 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
    }
}

// Használat
let data = try await NetworkManager.shared.fetchData(from: url)

Apple Singleton — az iOS SDK-ban sok objektum használ Singleton-t: UIApplication.shared, UIScreen.main, FileManager.default, NotificationCenter.default, UserDefaults.standard. Az Apple a Singleton-t fizikailag egyedi szolgáltatásokhoz (egy képernyő, egy alkalmazás) használja. A fejlesztők ezt a mintát másolják saját szolgáltatásaikhoz. A SwiftUI-ban a Singleton-hoz való globális hozzáférést az Environment és @EnvironmentObject váltja fel, ami javítja a tesztelhetőséget.

Singleton Androidban Kotlin nyelven: companion object és object

Kotlin Singleton — a legegyszerűbb mód: az object kulcsszó singleton-osztályt deklarál lusta inicializálással az első hozzáféréskor. A Kotlin object szálbiztos és nem igényel további szinkronizációt. Ha paraméteres konstruktorú Singleton-ra van szükség, companion object használatos lazy delegálttal. Androidban a Singleton gyakran szükséges az Application kontextushoz és az Application.onCreate()-on keresztül inicializált szolgáltatásokhoz.

kotlin
// 1. lehetőség: object — egyszerű Singleton paraméterek nélkül
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) }
}

// 2. lehetőség: companion object — Singleton paraméterekkel
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 — az Android számos rendszerszolgáltatása megvalósítja a Singleton-t: context.getSystemService(), Room.databaseBuilder(), Retrofit.Builder(). Példák közé tartozik a SharedPreferences, MediaPlayer, AudioManager. Android alkalmazásokban a Singleton gyakran használatos adattárakhoz, kezelőkhöz és gyárakhoz. A Google a Singleton DI-vel (Hilt, Koin) való helyettesítését ajánlja, ahol a Singleton hatókör (Scope.Singleton vagy @Singleton) a konténer által kezelt, az osztály pedig tesztelhető marad.

Thread safety: dispatch_once, synchronized és lock

Thread safety — kritikus követelmény a Singleton számára többszálú környezetben. Szinkronizáció nélkül két szál egyszerre ellenőrizheti az instance == null értéket, és két példányt hozhat létre. Megoldás — zárolás az első létrehozáskor és feloldás az inicializálás után. Swift-ben a statikus tulajdonságok (static let) alapértelmezés szerint szálbiztosak. Kotlin-ban az object szálbiztos. Java-stílushoz Kotlin-ban synchronized vagy @Volatile + double-check locking használatos.

NyelvMechanizmusSzálbiztonságLusta inicializálás
Swiftstatic letdispatch_once (automatikus)Igen, első hozzáféréskor
Kotlin objectObject declarationOsztályinicializáló szálbiztosIgen, első hozzáféréskor
Kotlin companionsynchronized + @VolatileDouble-checked lockingIgen, lazy-n vagy synchronized-en keresztül
Javasynchronized + volatileDouble-checked lockingIgen, getInstance()-ben

Double-checked locking — minta a Singleton lusta inicializálásához. Első ellenőrzés szinkronizáció nélkül (gyors, ha a példány már létezik), második — a synchronized blokkon belül (csak egy szál által létrehozva). A @Volatile garantálja a változások láthatóságát az összes szál számára. Volatile nélkül egy másik szál részben létrehozott objektumot láthat. Kotlin-ban a lazy delegált LazyThreadSafetyMode.SYNCHRONIZED módban automatikusan megvalósítja a double-checked locking-ot.

Singleton vs Dependency Injection: mikor használjuk

Dependency Injection — alternatíva a Singleton helyett az egyetlen példány kezelésére. A DI konténer (Dagger, Hilt, Koin, Swinject) egyszer hozza létre az objektumot a Singleton hatókörben, és konstruktoron keresztül injektálja. Az osztály nem tud a Singleton státuszáról — ezt a konténer dönti el. A kód tesztelhetővé válik: a tesztben a DI modul mock modulra cserélhető. A DI előnyei: explicit függőségek a konstruktorban, felülírhatóság, egységes életciklus.

Mikor indokolt a Singleton — rendszerszintű objektumok: Crashlytics, Analytics, Logging. Ezek a szolgáltatások egyszer inicializálódnak az AppDelegate/Application osztályban, és mindenhol használatosak. A DI túlzás számukra. A Singleton kényelmes képtárolókhoz is (NSCache, Coil, Glide), ahol a globális hozzáférés teljesítménnyel indokolt. Minden máshoz a DI előnyösebb: láthatóvá teszi a függőségeket, egyszerűsíti a tesztelést és a refaktorálást.

Hibrid megközelítés — Singleton felülírhatósági lehetőséggel a tesztekhez. Swift-ben protokoll + statikus tulajdonság használatos, amelyet a teszt felülírhat (pl. URLProtocol segítségével URLSession-hez). Kotlin-ban — nyitott osztály injectable tulajdonsággal, ahol a teszt reflexióval vagy setter-rel állítja be a mock-ot. Ez a megközelítés megőrzi a Singleton egyszerűségét, de eszközöket biztosít a teszteléshez. A Google a Hilt-et ajánlja Androidhoz, az Apple nem írja elő a DI-t iOS-hez — a választás a csapattól függ.

Gyakran Ismételt Kérdések

A Singleton anti-minta?

Nem, a Singleton GoF minta, de gyakori helytelen használata a Global State anti-mintává változtatja. A Singleton fizikailag egyedi erőforrásokhoz (képernyő, nyomtató, fájlrendszer) indokolt. Problémák akkor merülnek fel, amikor a Singleton-t adatkezelésre használják: rejtett függőségek, tesztelés nehézsége, a Single Responsibility Principle megsértése. Modern alternatíva — DI Singleton hatókörrel.

Hogyan teszteljük a Singleton-t használó kódot?

Három megközelítés: (1) protokollon keresztül — a Singleton megvalósít egy protokollt, a tesztek lecserélik a megvalósítást; (2) DI-n keresztül — a Singleton függőségként injektálódik a konstruktoron keresztül; (3) reset metóduson keresztül — a Singleton rendelkezik egy metódussal az állapot alaphelyzetbe állításához tesztekben (csak teszt buildhez). Az első megközelítés előnyösebb, a harmadik — veszélyes éles környezetben. A Swift lehetővé teszi a shared tulajdonság lecserélését runtime manipulációkkal a tesztekben.

Miben különbözik a Kotlin object a Java Singleton-tól?

A Kotlin object — nyelvi konstrukció, amely bytecode szinten hozza létre a Singleton-t. Ellentétben a Java megvalósítással, amely privát konstruktort és getInstance()-t használ, az object garantálja a szálbiztonságot, a lusta inicializálást és az öröklődés tilalmát. A Java Singleton manuális szinkronizációt (synchronized) és volatile-t igényel a többszálú környezetben való helyes működéshez. A Kotlin object — a legbiztonságosabb és legtömörebb mód Androidban.

Örökölhető a Singleton?

A Singleton öröklése megsérti a mintát: ha a Singleton osztály örökölhető, az alosztály létrehozhat egy második példányt, megsértve az egyediséget. Swift-ben a final class tiltja az öröklődést. A Kotlin object nem örökölhető (az object sealed). Ha variációval rendelkező Singleton-ra van szükség, használjon DI konténert Singleton hatókörrel: garantál egy példányt, és támogatja az öröklődést interfészeken keresztül.

Hogyan adjunk át paramétereket a Singleton-nak Androidban?

A paraméterek init(context: Application) vagy getInstance(param) segítségével adhatók át. A Kotlin object nem fogad paramétereket — használjon companion object-et getInstance(param) gyártó metódussal. A Hilt megoldja a problémát: @Singleton + @Inject constructor(context: Application) — a DI konténer automatikusan injektálja az Application kontextust. Retrofit kliens esetén a paraméterek (baseUrl, interceptors) a builder-en keresztül adhatók át a DI modulban.

Összefoglaló

  • Singleton — minta egyetlen példánnyal és globális hozzáféréssel
  • Swift shared — static let szálbiztonsággal a fordítótól
  • Kotlin object — lusta inicializálás további kód nélkül
  • Thread safety — double-checked locking Java-hoz, automatikus Swift/Kotlin esetén
  • Apple SDK — UIApplication.shared, UserDefaults.standard, FileManager.default
  • Android SDK — Retrofit, Room, SharedPreferences Singleton kezelőkön keresztül
  • Alternatívák — Dependency Injection a tesztelhető kódhoz

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is