Singleton — vad är det, enkel instans av en klass i iOS och Android

Författare: IT Sectr Publicerad: 2026-02-17 Lästid: 8 min

Singleton — ett konstruktionsmönster som garanterar en enda instans av en klass och tillhandahåller en global åtkomstpunkt till den. Singleton används ofta inom mobilutveckling för delade resurser: nätverksklienter, databaser, inställningshanterare. Mönstret beskrevs i den klassiska boken GoF (1994) och förblir ett av de mest igenkännbara. Mer — på Refactoring Guru: Singleton.

Huvudsakligt

  • Singleton — garanterar en instans av klassen i hela appen
  • Global åtkomstpunkt — statisk egenskap shared eller companion object
  • Thread safety — kräver synkronisering för korrekt funktion i flertrådad miljö
  • Kritik — Singleton försvårar testning och skapar dolda beroenden
  • Alternativ — Dependency Injection, Service Locator för att ersätta Singleton

Vad är Singleton: essensen av singleton-mönstret?

Singleton — ett konstruktionsmönster som beskrevs av GoF (Gang of Four) 1994. Mönstret löser två uppgifter: begränsar skapandet av klassinstansen till ett objekt och tillhandahåller global åtkomst till detta objekt. Singleton är användbart för resurser som måste vara unika: sessionsfabrik, bildcache, databasanlutningshanterare, Crashlytics eller Analytics-klient.

Implementering av Singleton kräver en privat konstruktor (förbjuder extern skapelse), ett statiskt fält med en enda instans och en statisk åtkomstmetod (shared, instance, getInstance). Klienter anropar Singleton.shared.method() utan att behöva oroa sig för att skapa objektet. Mönstret är populärt i iOS och Android: URLSession.shared, UserDefaults.standard, FirebaseApp.sharedInstance — allt detta är Singleton. Men överdriven användning av Singleton leder till anti-mönstret Global State.

Problem med Singleton — dolda beroenden (klasser är implicit beroende av Singleton-objektet), svårighet att testa (instansen kan inte ersättas i ett test utan extra ansträngning), brott mot Single Responsibility Principle (Singleton hanterar både sin instans och affärslogik). Modern mobilutveckling föredrar DI (Dagger, Hilt, Swinject) för att hantera enskilda instanser — DI-behållaren skapar objektet en gång och injicerar det via konstruktorn.

Singleton i iOS med Swift: shared och statiska egenskaper

Swift Singleton implementeras via en statisk shared-egenskap med en privat initierare. Från och med Swift 3 är lat initiering av statiska egenskaper garanterat trådsäker — kompilatorn lägger automatiskt till synkronisering via dispatch_once. Det räcker att deklarera static let shared = Class() och göra init() privat. Swift kräver ingen extra synkronisering för entrådad åtkomst efter initiering.

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
    }
}

// Användning
let data = try await NetworkManager.shared.fetchData(from: url)

Apple Singleton — i iOS SDK använder många objekt Singleton: UIApplication.shared, UIScreen.main, FileManager.default, NotificationCenter.default, UserDefaults.standard. Apple använder Singleton för tjänster som är fysiskt unika (en skärm, en app). Utvecklare kopierar detta mönster för sina egna tjänster. I SwiftUI ersätts global åtkomst till Singleton av Environment och @EnvironmentObject, vilket förbättrar testbarheten.

Singleton i Android med Kotlin: companion object och object

Kotlin Singleton — det enklaste sättet: nyckelordet object deklarerar en singleton-klass med lat initiering vid första åtkomst. Kotlin object är trådsäkert och kräver ingen extra synkronisering. Om en Singleton med konstruktorparametrar behövs, används companion object med lazy-delegat. I Android är Singleton ofta nödvändigt för Application-kontexten och tjänster som initieras via Application.onCreate().

kotlin
// Alternativ 1: object — enkel Singleton utan parametrar
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) }
}

// Alternativ 2: companion object — Singleton med parametrar
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 — många systemtjänster i Android implementerar Singleton: context.getSystemService(), Room.databaseBuilder(), Retrofit.Builder(). Exempel inkluderar SharedPreferences, MediaPlayer, AudioManager. I Android-appar används Singleton ofta för datalager, hanterare och fabriker. Google rekommenderar att ersätta Singleton med DI (Hilt, Koin), där Singleton-scope (Scope.Singleton eller @Singleton) hanteras av behållaren och klassen förblir testbar.

Thread safety: dispatch_once, synchronized och lock

Thread safety — ett kritiskt krav för Singleton i flertrådad miljö. Utan synkronisering kan två trådar samtidigt kontrollera instance == null och skapa två instanser. Lösning — låsning vid första skapelse och frigöring efter initiering. I Swift är statiska egenskaper (static let) som standard trådsäkra. I Kotlin är object trådsäkert. För Java-stil i Kotlin används synchronized eller @Volatile + double-check locking.

SpråkMekanismTrådsäkerhetLat initiering
Swiftstatic letdispatch_once (auto)Ja, vid första åtkomst
Kotlin objectObject declarationKlassinitieraren är trådsäkerJa, vid första åtkomst
Kotlin companionsynchronized + @VolatileDouble-checked lockingJa, via lazy eller synchronized
Javasynchronized + volatileDouble-checked lockingJa, i getInstance()

Double-checked locking — mönster för lat initiering av Singleton. Första kontroll utan synkronisering (snabb, om instansen redan finns), andra — inuti synchronized (skapande endast av en tråd). @Volatile garanterar synlighet av ändringar för alla trådar. Utan volatile kan en annan tråd se ett delvis skapat objekt. I Kotlin implementerar lazy-delegaten med LazyThreadSafetyMode.SYNCHRONIZED automatiskt double-checked locking.

Singleton vs Dependency Injection: när man ska använda

Dependency Injection — ett alternativ till Singleton för att hantera en enda instans. DI-behållaren (Dagger, Hilt, Koin, Swinject) skapar objektet en gång i Singleton-scope och injicerar det via konstruktorn. Klassen vet inte om sin Singleton-status — detta avgörs av behållaren. Koden blir testbar: i testet ersätts DI-modulen med en mock-modul. Fördelar med DI: explicita beroenden i konstruktorn, möjlighet att åsidosätta, enhetlig livscykel.

När Singleton är motiverat — objekt på systemnivå: Crashlytics, Analytics, Logging. Dessa tjänster initieras en gång i AppDelegate/Application och används överallt. DI är överdrivet för dem. Singleton är också bekvämt för bildcachar (NSCache, Coil, Glide), där global åtkomst är motiverad av prestanda. För allt annat är DI att föredra: det gör beroenden synliga, förenklar testning och omfaktorering.

Hybridmetod — Singleton med möjlighet till åsidosättande för tester. I Swift används ett protokoll + en statisk egenskap som testet kan ersätta (t.ex. via URLProtocol för URLSession). I Kotlin — en öppen klass med injectable-egenskap, där testet ställer in mock via reflektion eller setter. Detta tillvägagångssätt bevarar Singleton-enkelheten men ger verktyg för testning. Google rekommenderar Hilt för Android, Apple påtvingar inte DI för iOS — valet beror på teamet.

Vanliga frågor

Är Singleton ett anti-mönster?

Nej, Singleton är ett GoF-mönster, men dess frekventa felaktiga användning gör det till anti-mönstret Global State. Singleton är motiverat för fysiskt unika resurser (skärm, skrivare, filsystem). Problem uppstår när Singleton används för datahantering: dolda beroenden, svårighet att testa, brott mot Single Responsibility Principle. Modernt alternativ — DI med Singleton-scope.

Hur testar man kod som använder Singleton?

Tre tillvägagångssätt: (1) via protokoll — Singleton implementerar ett protokoll, tester ersätter implementeringen; (2) via DI — Singleton injiceras som ett beroende via konstruktorn; (3) via reset-metod — Singleton har en metod för att återställa tillstånd i tester (endast för testbyggen). Det första tillvägagångssättet är att föredra, det tredje — farligt för produktion. Swift tillåter att shared-egenskapen ersätts via runtime-manipulationer i tester.

Hur skiljer sig Kotlin object från Java Singleton?

Kotlin object — en språkkonstruktion som skapar Singleton på bytekodnivå. Till skillnad från Java-implementeringen med privat konstruktor och getInstance(), garanterar object trådsäkerhet, lat initiering och arvsförbud. Java Singleton kräver manuell synkronisering (synchronized) och volatile för korrekt funktion i flertrådad miljö. Kotlin object — det säkraste och mest koncisa sättet i Android.

Kan Singleton ärvas?

Arv av Singleton bryter mot mönstret: om Singleton-klassen kan ärvas kan underklassen skapa en andra instans, vilket bryter uniciteten. I Swift förbjuder final class arv. Kotlin object kan inte ärvas (object — sealed). Om en Singleton med variation behövs, använd en DI-behållare med Singleton-scope: den garanterar en instans och stödjer arv via gränssnitt.

Hur skickar man parametrar till Singleton i Android?

Parametrar skickas via init(context: Application) eller getInstance(param). Kotlin object accepterar inte parametrar — använd companion object med en fabriksmetod getInstance(param). Hilt löser problemet: @Singleton + @Inject constructor(context: Application) — DI-behållaren injicerar Application-kontexten automatiskt. För retrofit-klienten skickas parametrar (baseUrl, interceptors) via builder i DI-modulen.

Sammanfattning

  • Singleton — mönster med enda instans och global åtkomst
  • Swift shared — static let med trådsäkerhet från kompilatorn
  • Kotlin object — lat initiering utan extra kod
  • Thread safety — double-checked locking för Java, automatiskt för Swift/Kotlin
  • Apple SDK — UIApplication.shared, UserDefaults.standard, FileManager.default
  • Android SDK — Retrofit, Room, SharedPreferences via Singleton-hanterare
  • Alternativ — Dependency Injection för testbar kod

Vi utvecklar en mobil applikation nyckelfärdigt

IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.

Diskutera projektet

Läs också