Callback — vad är det, återanropsfunktioner och hur de fungerar

Författare: IT Sectr Publicerad: 2026-03-17 Lästid: 11 min

Callback — är en funktion som skickas till en annan funktion som argument och exekveras efter att en asynkron operation slutförts. I mobil utveckling används callback för att bearbeta resultat från nätverksförfrågningar, arbete med databaser och animationer. Enligt Apple Documentation (2025) är closures i Swift den primära formen av callback och används i URLSession, GCD och Combine. I Android implementeras callback via gränssnitt, Kotlin-lambdas och ListenableFuture.

Huvudpunkter

  • Callback — återanropsfunktion som skickas som argument för asynkron exekvering.
  • Swift använder closures med nyckelordet @escaping för callback.
  • Kotlin tillämpar lambda-uttryck och funktioner av högre ordning för callback.
  • Retain cycle — minnesläcka vid infångning av self i callback på iOS.
  • Callback Hell — problemet med nästlade callback, löst via async/await och korutiner.

Vad är Callback?

Callback (återanropsfunktion) — är exekverbar kod som skickas till en annan funktion och anropas efter att en specifik åtgärd slutförts. I mobil utveckling är callback den grundläggande mekanismen för asynkron programmering, som möjliggör reaktion på slutförande av nätverksförfrågningar, timers, animationer och in-/utmatningsoperationer utan att blockera huvudtråden. Swift och Kotlin tillhandahåller inbyggda syntaktiska konstruktioner för att skapa callback — closures respektive lambdas.

Callback-principen

En funktion av högre ordning accepterar en annan funktion som parameter och anropar den efter att ha utfört sin huvudlogik. Kontrollflödet återgår till anroparen via callback, därav namnet. I iOS tillämpas callback i UIKit (UIView.animate-animationer), Foundation (URLSession.dataTask) och Combine (sink). I Android används callback i View.OnClickListener, Retrofit Callback och Room DAO. Moderna API:er ersätter allt oftare callback med async/await eller korutiner, men förståelse av callback är nödvändig för arbete med äldre kod och lågnivå-API:er.

Synkron och asynkron Callback

Callback kan vara synkron (anropas omedelbart inuti funktionen) och asynkron (anropas senare från en annan tråd eller kö). Synkrona callback används för sortering (komparatorer) och genomgång av samlingar. Asynkrona callback tillämpas för nätverksförfrågningar, filläsning och arbete med sensorer. Skillnaden är avgörande för att förstå trådhantering: synkron callback exekveras i samma tråd, asynkron — i tråden som bestäms av dispatchern (DispatchQueue i iOS, Dispatchers i Kotlin).

Hur fungerar Callback i iOS och Android?

Callback-mekanismen på båda plattformarna bygger på samma princip: funktionen skickas som ett förstklassigt objekt och lagras tills exekveringstillfället. Implementeringarna skiljer sig dock på grund av olika språkparadigm. I iOS är callback en closure som fångar variabler från den omgivande kontexten. I Android implementeras callback oftast via anonyma klasser eller Kotlin lambda-uttryck, kompilerade till FunctionalInterface.

Callback-livscykel i iOS

När en asynkron funktion anropas lagras closuren på heapen tillsammans med infångade variabler. När operationen slutförs placerar GCD- eller OperationQueue-systemet callbacken i lämplig kö (main queue eller background queue). Efter exekvering tas callbacken bort från minnet om det inte finns några starka referenser. Capture list ([weak self]) förhindrar att objektet hålls kvar efter deallokering. Utan capture list uppstår en retain cycle där objektet och callbacken refererar till varandra.

swift
func fetchData(completion: @escaping (Result<Data, Error>) -> Void) {
    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        if let error = error {
            completion(.failure(error))
            return
        }
        completion(.success(data))
    }
    task.resume()
}

// Användning med [weak self]
fetchData { [weak self] result in
    guard let self else { return }
    switch result {
    case .success(let data):
        self.updateUI(data)
    case .failure(let error):
        self.showError(error)
    }
}

Callback-livscykel i Android

I Android skickas callback via ett gränssnitt eller en lambda. När en asynkron operation utförs via ExecutorService eller en korutin lagras callbacken i minnet tills bakgrundsarbetet slutförts. Kotlin-lambdas kompileras till anonyma klasser som fångar externa variabler. Avsaknaden av svaga referenser i JVM kräver manuell hantering: återställning av callback i onDestroy() eller annullering av korutiner via Job.cancel(). ViewModel och LiveData löser detta problem på arkitekturnivå.

kotlin
interface Callback<T> {
    fun onSuccess(data: T)
    fun onError(error: Throwable)
}

class Repository {
    fun loadData(callback: Callback<List<User>>) {
        thread {
            try {
                val result = api.fetchUsers()
                runOnUiThread { callback.onSuccess(result) }
            } catch (e: Exception) {
                runOnUiThread { callback.onError(e) }
            }
        }
    }
}

// Användning med lambda
repository.loadData(object : Callback<List<User>> {
    override fun onSuccess(data: List<User>) { showUsers(data) }
    override fun onError(error: Throwable) { showError(error.message) }
})

Callback-syntax i Swift och Kotlin

Callback-syntaxen bestäms av språkets förmåga att arbeta med funktioner som förstklassiga objekt. I Swift har closures en kortfattad syntax med automatiska argumentnamn ($0, $1). I Kotlin stöder lambdas också it för ett enskilt argument. Skillnaderna manifesteras i hanteringen av variabelfångst (capture list i Swift vs föränderliga referenser i Kotlin) och typning (Result<Success, Failure> vs Result<T>).

Callback i Swift: closures

En Swift-closure är ett självständigt kodblock som kan skickas och användas i en annan funktion. Closures kan vara globala (namngivna), nästlade och expression-level. @escaping markerar en closure som kommer att exekveras efter återkomst från funktionen — detta är ett obligatoriskt krav för asynkrona callback. Utan @escaping kan closuren endast exekveras inuti funktionskroppen. Trailing closure-syntax gör det möjligt att skicka closuren efter de runda parenteserna: fetchData { result in ... }.

swift
typealias NetworkResult = (Result<[String: Any], Error>) -> Void

func performRequest(
    url: URL,
    then handler: @escaping NetworkResult
) {
    let task = URLSession.shared.dataTask(with: url) { data, _, error in
        handler(Result {
            guard let json = try JSONSerialization.jsonObject(with: data)
            else { throw NetworkError.invalidData }
            return json as! [String: Any]
        })
    }
    task.resume()
}

performRequest(url: url) { result in
    switch result {
    case .success(let json): process(json)
    case .failure(let error): log(error.localizedDescription)
    }
}

Callback i Kotlin: lambdas och funktioner av högre ordning

Kotlin stöder funktioner av högre ordning som accepterar andra funktioner som parametrar. Callback i Kotlin skickas via en parameter av typ (T) -> Unit eller (T) -> R för returvärde. Suspend-funktionerna i Kotlin-korutiner ersätter callback med sekventiell kod, men callback finns kvar i Java-kompatibla API:er och Android SDK (View.setOnClickListener, TextWatcher). Kotlin-lambdas fångar automatiskt val-variabler, var-variabler kräver föränderliga omslag.

kotlin
fun <T, R> processWithCallback(
    input: T,
    transform: (T) -> R,
    onResult: (R) -> Unit
) {
    thread {
        val result = transform(input)
        runOnUiThread { onResult(result) }
    }
}

// Exempel med lambda
processWithCallback(
    input = "Hello",
    transform = { it.length },
    onResult = { length ->
        textView.text = "Length: $length"
    }
)

Retain cycles och minnesläckor i Callback

Retain cycle — en situation där två objekt håller starka referenser till varandra, vilket förhindrar att de frigörs av sophämtaren. I Swift uppstår en retain cycle när en viewController fångar en closure och closuren fångar self. I Kotlin/Java uppstår en läcka när Activity skickar en inre klass eller lambda till en långvarig bakgrundsoperation. Enligt WWDC Session 10216 (2024) är felaktig hantering av closures den tredje vanligaste orsaken till minnesläckor i iOS-applikationer.

Retain cycles i Swift

Swift använder Automatic Reference Counting (ARC), som frigör objektet när referensräknaren nollställs. Capture list [weak self] eller [unowned self] i closuren förhindrar retain cycle. weak self skapar en valfri referens som blir nil vid deallokering av objektet. unowned self antar att objektet lever längre än closuren — brott mot detta antagande orsakar en krasch. Användning av weak self som säkert standardalternativ rekommenderas.

swift
class DataController {
    var onDataUpdate: ((String) -> Void)?

    func setupCallback() {
        // Retain cycle!
        onDataUpdate = { text in
            self.process(text)
        }

        // Korrigerat: [weak self]
        onDataUpdate = { [weak self] text in
            guard let self else { return }
            self.process(text)
        }
    }

    func process(_ input: String) { }
}

Minnesläckor i Android

I Android uppstår en callback-läcka när Activity eller Fragment skickar en lyssnare till en singleton-komponent (t.ex. EventBus eller Service). WeakReference tillåter sophämtaren att frigöra Activity, även om det finns en svag referens till den. Lifecycle-aware komponenter (LiveData, Flow) löser problemet automatiskt. Kotlin-lambdas som fångar Activity-kontext kan också orsaka läcka: lambdan håller implicit en referens till this.

kotlin
class SafeCallbackManager {
    private val listeners = mutableListOf<WeakReference<(String) -> Unit>>()

    fun addListener(callback: (String) -> Unit) {
        listeners.add(WeakReference(callback))
    }

    fun notifyAll(data: String) {
        val iterator = listeners.iterator()
        while (iterator.hasNext()) {
            val ref = iterator.next().get()
            if (ref != null) ref(data)
            else iterator.remove()
        }
    }
}

// Användning i Fragment
manager.addListener { result ->
    // WeakReference behåller inte Fragment
    updateUI(result)
}

Callback Hell och sätt att bekämpa det

Callback Hell (även känt som Undergångens Pyramid) — en situation där många nästlade callback skapar en djupt nästlad kodstruktur som är svår att läsa och felsöka. Varje nästa steg kräver väntan på att det föregående ska slutföras, vilket leder till 5-10 nivåer av nästling. Detta problem är karakteristiskt för sekventiella asynkrona operationer: ladda data → tolka → spara i databas → uppdatera UI.

Lösningar i Swift: async/await

Swift 5.5 introducerade asynkrona funktioner (async/await) som gör det möjligt att skriva asynkron kod sekventiellt. AsyncSequence och AsyncStream ersätter callback-baserade iterationer. Combine-ramverket tillhandahåller operatorer flatMap, merge, combineLatest för komposition av asynkrona flöden utan nästling. Callback förblir dock nödvändig för arbete med Objective-C API:er och tredjepartsbibliotek utan async-stöd.

swift
// Nästlade callback — Callback Hell
loginUser(credentials) { user in
    fetchProfile(user.id) { profile in
        downloadAvatar(profile.avatarUrl) { image in
            cacheImage(image) { success in
                updateUI(user, profile, image)
            }
        }
    }
}

// async/await — lösning
func loadUserExperience() async throws {
    let user = try await loginUser(credentials)
    let profile = try await fetchProfile(user.id)
    let image = try await downloadAvatar(profile.avatarUrl)
    try await cacheImage(image)
    updateUI(user, profile, image)
}

Lösningar i Kotlin: korutiner och Flow

Kotlin-korutiner ersätter callback med suspend-funktioner med sekventiell exekvering. Flow tillhandahåller kalla flöden med operatorerna map, flatMapConcat, combine. CoroutineScope gör det möjligt att annullera alla startade korutiner vid komponentens förstörelse. Room, Retrofit och andra Jetpack-bibliotek har inbyggt stöd för suspend-funktioner, vilket eliminerar behovet av callback för standardoperationer.

kotlin
// Sekventiella callback — Callback Hell
api.login(credentials) { user ->
    api.fetchProfile(user.id) { profile ->
        api.download(profile.avatarUrl) { bytes ->
            file.save(bytes) { result ->
                textView.text = result.toString()
            }
        }
    }
}

// Korutiner — lösning
suspend fun loadUserData() {
    val user = withContext(Dispatchers.IO) { api.login(credentials) }
    val profile = withContext(Dispatchers.IO) { api.fetchProfile(user.id) }
    val bytes = withContext(Dispatchers.IO) { api.download(profile.avatarUrl) }
    withContext(Dispatchers.IO) { file.save(bytes) }
    textView.text = "Done"
}

Callback vs Delegate: vad ska man välja?

Callback och Delegate — två metoder för asynkron notifiering, och valet mellan dem beror på arkitekturkraven. Callback är lämplig för engångsoperationer med ett enda resultat. Delegate är avsedd för flera händelser med olika metodsignaturer. Apple rekommenderar delegate för komplexa protokoll med flera metoder, callback — för enkla closures med ett enda resultat. I Android ersätter callback delegate i de flesta fall på grund av stöd för lambdas.

När ska man välja Callback

Callback är optimal för operationer med ett enda resultat: nätverksförfrågan, filläsning, animation med ett completion-block. Fördelar: kompakt syntax, inget separat protokoll, direkt kontextinfångning. Nackdelar: komplexitet vid flera resultat (förlopp, paus, annullering), omöjlighet att skicka flera gånger (om callback kan anropas mer än en gång — använd en publisher).

När ska man välja Delegate

Delegate är lämplig för protokoll med flera obligatoriska och valfria metoder: UITableViewDelegate, CLLocationManagerDelegate, Bluetooth-anslutningar. Fördelar: tydlig typning av varje metod, dokumentation via protokoll, stöd för valfria metoder via @objc optional. Nackdelar: boilerplate-kod, obligatorisk svag referens till delegate (weak var delegate), komplexitet vid kontextinfångning.

Vanliga frågor

Vad är skillnaden mellan callback och en funktion av högre ordning?

Callback är ett specialfall av en funktion av högre ordning. En funktion av högre ordning accepterar en annan funktion som argument eller returnerar den. Callback är en funktion som specifikt skickas för asynkron exekvering efter att operationen slutförts. Alla callback implementeras via funktioner av högre ordning, men inte varje funktion av högre ordning är en callback.

Kan en callback anropas flera gånger?

Enligt konvention ska en callback anropas exakt en gång — antingen success eller failure. Flera anrop av samma callback anses vara ett designfel. För flera händelser (förlopp, dataström) använd Observable, Publisher eller Flow — de stöder flera värdeutsläpp. Vissa API:er bryter mot denna regel, vilket leder till svårupptäckta buggar.

Vad är trailing closure i Swift?

Trailing closure — syntaktiskt socker i Swift som gör det möjligt att skicka en closure efter de runda parenteserna i ett funktionsanrop. Om en funktion accepterar en closure som sista argument kan den placeras utanför parenteserna: fetchData { result in ... }. För flera closures tillämpas trailing closure endast på den sista, de övriga namnges inom parenteserna. Detta förbättrar läsbarheten av callback-baserade API:er.

Hur undviker man minnesläckor vid callback i Android?

Använd WeakReference för långlivade lyssnare, annullera korutiner via Job.cancel() i onDestroy(), tillämpa lifecycleScope för automatisk annullering. ViewModel + LiveData/Flow löser problemet på arkitekturnivå. Undvik att skicka Activity-kontext till statiska callback — använd Application-kontext. Kotlin-lambdas fångar implicit this, kontrollera med memory profiler.

Kommer async/await att helt ersätta callback?

Async/await ersätter callback för sekventiell asynkron kod, men inte för event-driven arkitektur. Callback finns kvar i system-API:er (View.OnClickListener, URLSession-delegater), återanrop med förlopp och tredjepartsbibliotek. Fullständig ersättning är omöjlig på grund av bakåtkompatibilitet. Den moderna strategin är att använda async/await med callback-omslag (continuation i Swift, suspendCancellableCoroutine i Kotlin).

Sammanfattning

  • Callback — återanropsfunktion som skickas som argument för asynkron exekvering efter att operationen slutförts.
  • Swift implementerar callback via closures med @escaping, capture list [weak self] och trailing closure-syntax.
  • Kotlin använder lambdas, funktioner av högre ordning och korutiners suspend-funktioner för asynkronitet.
  • Retain cycle i iOS förhindras med capture list; i Android — med WeakReference och Lifecycle-aware komponenter.
  • Callback Hell löses via async/await i Swift och korutiner med Flow i Kotlin.
  • Delegate är att föredra framför callback för protokoll med flera metoder; callback — för engångsoperationer.
  • Använd callback för enkla asynkrona operationer, async/await för sekventiella kedjor, delegate för flera händelser.

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å