Callback — co to je, funkce zpětného volání a jak fungují

Autor: IT Sectr Publikováno: 2026-03-17 Doba čtení: 11 min

Callback — je funkce, která je předána jiné funkci jako argument a je provedena po dokončení asynchronní operace. V mobilním vývoji se callback používá pro zpracování výsledků síťových požadavků, práci s databázemi a animace. Podle Apple Documentation (2025) jsou closures ve Swift hlavní formou callback a používají se v URLSession, GCD a Combine. V Androidu je callback implementován přes rozhraní, lambda výrazy Kotlin a ListenableFuture.

Hlavní body

  • Callback — funkce zpětného volání předávaná jako argument pro asynchronní provedení.
  • Swift používá closures s klíčovým slovem @escaping pro callback.
  • Kotlin aplikuje lambda výrazy a funkce vyššího řádu pro callback.
  • Retain cycle — únik paměti při zachycení self v callback na iOS.
  • Callback Hell — problém vnořených callback, řešený přes async/await a korutiny.

Co je Callback?

Callback (funkce zpětného volání) — je spustitelný kód, který je předán jiné funkci a je volán po dokončení určité akce. V mobilním vývoji je callback základním mechanismem asynchronního programování, umožňujícím reagovat na dokončení síťových požadavků, časovačů, animací a vstupně-výstupních operací bez blokování hlavního vlákna. Swift a Kotlin poskytují vestavěné syntaktické konstrukce pro vytváření callback — closures a lambdy.

Princip fungování Callback

Funkce vyššího řádu přijímá jinou funkci jako parametr a volá ji po provedení své hlavní logiky. Tok řízení se vrací volajícímu přes callback, odtud název. V iOS se callback používá v UIKit (animace UIView.animate), Foundation (URLSession.dataTask) a Combine (sink). V Android se callback používá v View.OnClickListener, Retrofit Callback a Room DAO. Moderní API stále častěji nahrazují callback async/await nebo korutinami, ale porozumění callback je nezbytné pro práci s legacy kódem a nízkoúrovňovými API.

Synchronní a asynchronní Callback

Callback může být synchronní (volán okamžitě uvnitř funkce) a asynchronní (volán později z jiného vlákna nebo fronty). Synchronní callback se používají pro řazení (komparátory) a procházení kolekcí. Asynchronní callback se aplikují pro síťové požadavky, čtení souborů a práci se senzory. Rozdíl je kritický pro pochopení threading: synchronní callback se provádí ve stejném vlákně, asynchronní — ve vlákně určeném dispečerem (DispatchQueue v iOS, Dispatchers v Kotlin).

Jak funguje Callback v iOS a Android?

Mechanismus callback na obou platformách je založen na stejném principu: funkce je předána jako objekt první třídy a uložena až do okamžiku provedení. Implementace se však liší kvůli různým jazykovým paradigmům. V iOS je callback closure, které zachycuje proměnné z okolního kontextu. V Android je callback nejčastěji implementován přes anonymní třídy nebo lambda výrazy Kotlin, kompilované do FunctionalInterface.

Životní cyklus Callback v iOS

Při volání asynchronní funkce je closure uloženo na haldě spolu se zachycenými proměnnými. Když je operace dokončena, systém GCD nebo OperationQueue umístí callback do příslušné fronty (main queue nebo background queue). Po provedení je callback odstraněn z paměti při absenci silných referencí. Capture list ([weak self]) zabraňuje udržení objektu po jeho dealokaci. Bez capture list vzniká retain cycle, kde objekt a callback na sebe vzájemně odkazují.

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

// Použití s [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)
    }
}

Životní cyklus Callback v Android

V Androidu je callback předán přes rozhraní nebo lambdu. Při provádění asynchronní operace přes ExecutorService nebo korutinu je callback uložen v paměti až do dokončení práce na pozadí. Kotlin lambdy jsou kompilovány do anonymních tříd, které zachycují vnější proměnné. Absence slabých referencí v JVM vyžaduje ruční správu: vynulování callback v onDestroy() nebo zrušení korutin přes Job.cancel(). ViewModel a LiveData řeší tento problém na úrovni architektonické komponenty.

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

// Použití s lambdou
repository.loadData(object : Callback<List<User>> {
    override fun onSuccess(data: List<User>) { showUsers(data) }
    override fun onError(error: Throwable) { showError(error.message) }
})

Syntaxe Callback ve Swift a Kotlin

Syntaxe callback je určena jazykovými možnostmi práce s funkcemi jako objekty první třídy. Ve Swift mají closures stručnou syntaxi s automatickými názvy argumentů ($0, $1). V Kotlin lambdy také podporují it pro jediný argument. Rozdíly se projevují ve zpracování zachycení proměnných (capture list ve Swift vs měnitelné reference v Kotlin) a typizaci (Result<Success, Failure> vs Result<T>).

Callback ve Swift: closures

Swift closure je soběstačný blok kódu, který může být předán a použit v jiné funkci. Closures mohou být globální (pojmenované), vnořené a expression-level. @escaping označuje closure, které bude provedeno po návratu z funkce — to je povinný požadavek pro asynchronní callback. Bez @escaping může být closure provedeno pouze uvnitř těla funkce. Trailing closure syntax umožňuje předat closure za kulaté závorky: 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 v Kotlin: lambdy a funkce vyššího řádu

Kotlin podporuje funkce vyššího řádu, které přijímají jiné funkce jako parametry. Callback v Kotlin je předán přes parametr typu (T) -> Unit nebo (T) -> R pro návratovou hodnotu. Suspend funkce Kotlin korutin nahrazují callback sekvenčním kódem, ale callback zůstává v Java-kompatibilních API a Android SDK (View.setOnClickListener, TextWatcher). Kotlin lambdy automaticky zachycují val proměnné, var proměnné vyžadují měnitelné obaly.

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

// Příklad s lambdou
processWithCallback(
    input = "Hello",
    transform = { it.length },
    onResult = { length ->
        textView.text = "Length: $length"
    }
)

Retain cycles a úniky paměti v Callback

Retain cycle — situace, kdy dva objekty drží silné reference na sebe navzájem, čímž brání jejich uvolnění garbage collectorem. Ve Swift retain cycle vzniká, když viewController zachytí closure a closure zachytí self. V Kotlin/Java dochází k úniku, když Activity předá vnitřní třídu nebo lambdu dlouhotrvající operaci na pozadí. Podle WWDC Session 10216 (2024) je nesprávné řízení closures třetí nejčastější příčinou úniků paměti v iOS aplikacích.

Retain cycles ve Swift

Swift používá Automatic Reference Counting (ARC), který uvolní objekt při vynulování počítadla referencí. Capture list [weak self] nebo [unowned self] v closures zabraňuje retain cycle. weak self vytváří volitelnou referenci, která se stane nil při dealokaci objektu. unowned self předpokládá, že objekt žije déle než closure — porušení tohoto předpokladu způsobí crash. Použití weak self jako bezpečné výchozí možnosti se doporučuje.

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

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

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

    func process(_ input: String) { }
}

Úniky paměti v Android

V Androidu dochází k úniku callback, když Activity nebo Fragment předá posluchače komponentě singleton (např. EventBus nebo Service). WeakReference umožňuje garbage collectoru uvolnit Activity, i když na ni existuje slabá reference. Lifecycle-aware komponenty (LiveData, Flow) řeší problém automaticky. Kotlin lambdy zachycující kontext Activity mohou také způsobit únik: lambda implicitně uchovává referenci na 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()
        }
    }
}

// Použití ve Fragmentu
manager.addListener { result ->
    // WeakReference neudržuje Fragment
    updateUI(result)
}

Callback Hell a způsoby boje

Callback Hell (také známý jako Pyramida zkázy) — situace, kdy mnoho vnořených callback vytváří hluboce vnořenou strukturu kódu, obtížně čitelnou a laditelnou. Každý další krok vyžaduje čekání na dokončení předchozího, což vede k 5-10 úrovním vnoření. Tento problém je charakteristický pro sekvenční asynchronní operace: načtení dat → parsování → uložení do databáze → aktualizace UI.

Řešení ve Swift: async/await

Swift 5.5 zavedl asynchronní funkce (async/await), které umožňují psát asynchronní kód sekvenčně. AsyncSequence a AsyncStream nahrazují iterace založené na callback. Framework Combine poskytuje operátory flatMap, merge, combineLatest pro kompozici asynchronních toků bez vnoření. Callback však zůstává nezbytný pro práci s Objective-C API a knihovnami třetích stran bez podpory async.

swift
// Vnořené 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 — řešení
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)
}

Řešení v Kotlin: korutiny a Flow

Kotlin korutiny nahrazují callback suspend funkcemi se sekvenčním prováděním. Flow poskytuje cold toky s operátory map, flatMapConcat, combine. CoroutineScope umožňuje zrušit všechny spuštěné korutiny při zničení komponenty. Room, Retrofit a další Jetpack knihovny mají vestavěnou podporu pro suspend funkce, čímž eliminují potřebu callback pro standardní operace.

kotlin
// Sekvenční 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()
            }
        }
    }
}

// Korutiny — řešení
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: co vybrat?

Callback a Delegate — dva přístupy k asynchronnímu oznámení a výběr mezi nimi závisí na architektonických požadavcích. Callback je vhodný pro jednorázové operace s jedním výsledkem. Delegate je určen pro vícenásobné události s různými podpisy metod. Apple doporučuje delegate pro složité protokoly s několika metodami, callback — pro jednoduchá closures s jedním výsledkem. V Androidu callback nahrazuje delegate ve většině případů kvůli podpoře lambd.

Kdy vybrat Callback

Callback je optimální pro operace s jediným výsledkem: síťový požadavek, čtení souboru, animace s completion blokem. Výhody: kompaktní syntaxe, žádný samostatný protokol, přímé zachycení kontextu. Nevýhody: složitost při vícenásobných výsledcích (průběh, pauza, zrušení), nemožnost vícenásobného odeslání (pokud může být callback volán více než jednou — použijte publisher).

Kdy vybrat Delegate

Delegate je vhodný pro protokoly s několika povinnými a volitelnými metodami: UITableViewDelegate, CLLocationManagerDelegate, Bluetooth připojení. Výhody: jasná typizace každé metody, dokumentace přes protokol, podpora volitelných metod přes @objc optional. Nevýhody: boilerplate kód, povinná slabá reference na delegate (weak var delegate), složitost při zachycení kontextu.

Často kladené otázky

Jaký je rozdíl mezi callback a funkcí vyššího řádu?

Callback je zvláštní případ funkce vyššího řádu. Funkce vyššího řádu přijímá jinou funkci jako argument nebo ji vrací. Callback je funkce předaná speciálně pro asynchronní provedení po dokončení operace. Všechny callback jsou implementovány přes funkce vyššího řádu, ale ne každá funkce vyššího řádu je callback.

Může být callback volán vícekrát?

Podle konvence by callback měl být volán přesně jednou — buď success nebo failure. Vícenásobné volání stejného callback je považováno za chybu návrhu. Pro vícenásobné události (průběh, datový tok) použijte Observable, Publisher nebo Flow — podporují vícenásobnou emisi hodnot. Některá API toto pravidlo porušují, což vede k obtížně odhalitelným chybám.

Co je trailing closure ve Swift?

Trailing closure — syntaktický cukr Swift, který umožňuje předat closure za kulaté závorky volání funkce. Pokud funkce přijímá closure jako poslední argument, lze jej umístit mimo závorky: fetchData { result in ... }. Pro několik closures se trailing closure aplikuje pouze na poslední, ostatní se pojmenují uvnitř závorek. To zlepšuje čitelnost API založených na callback.

Jak se vyhnout úniku paměti při callback v Android?

Použijte WeakReference pro dlouho žijící posluchače, rušte korutiny přes Job.cancel() v onDestroy(), aplikujte lifecycleScope pro automatické zrušení. ViewModel + LiveData/Flow řeší problém na architektonické úrovni. Vyhněte se předávání kontextu Activity do statických callback — použijte kontext aplikace. Kotlin lambdy implicitně zachycují this, kontrolujte pomocí memory profileru.

Nahradí async/await callback úplně?

Async/await nahrazuje callback pro sekvenční asynchronní kód, ale ne pro event-driven architekturu. Callback zůstává v systémových API (View.OnClickListener, delegáti URLSession), zpětných voláních s průběhem a knihovnách třetích stran. Úplné nahrazení je nemožné kvůli zpětné kompatibilitě. Moderní strategií je použití async/await s callback obaly (continuation ve Swift, suspendCancellableCoroutine v Kotlin).

Shrnutí

  • Callback — funkce zpětného volání předaná jako argument pro asynchronní provedení po dokončení operace.
  • Swift implementuje callback přes closures s @escaping, capture list [weak self] a trailing closure syntaxí.
  • Kotlin používá lambdy, funkce vyššího řádu a suspend funkce korutin pro asynchronnost.
  • Retain cycle v iOS je zabráněno capture list; v Android — WeakReference a Lifecycle-aware komponentami.
  • Callback Hell je řešen přes async/await ve Swift a korutiny s Flow v Kotlin.
  • Delegate je preferován před callback pro protokoly s několika metodami; callback — pro jednorázové operace.
  • Použijte callback pro jednoduché asynchronní operace, async/await pro sekvenční řetězce, delegate pro vícenásobné události.

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také