Callback — ano ito, mga function ng tawag pabalik at kung paano gumagana

May-akda: IT Sectr Nai-publish: 2026-03-17 Oras ng pagbabasa: 11 min

Callback — ay isang function na ipinapasa sa ibang function bilang argumento at isinasagawa pagkatapos makumpleto ang isang asynchronous na operasyon. Sa mobile development, ang callback ay ginagamit para sa pagproseso ng mga resulta ng network request, pagtatrabaho sa mga database, at mga animation. Ayon sa Apple Documentation (2025), ang mga closure sa Swift ay ang pangunahing anyo ng callback at ginagamit sa URLSession, GCD, at Combine. Sa Android, ang callback ay ipinapatupad sa pamamagitan ng mga interface, Kotlin lambda, at ListenableFuture.

Mga Pangunahing Punto

  • Callback — function ng tawag pabalik na ipinapasa bilang argumento para sa asynchronous na pagpapatupad.
  • Swift gumagamit ng mga closure na may keyword na @escaping para sa callback.
  • Kotlin naglalapat ng mga lambda expression at higher-order function para sa callback.
  • Retain cycle — pagtagas ng memory kapag kinukuha ang self sa callback sa iOS.
  • Callback Hell — problema ng nested callback, nalutas sa pamamagitan ng async/await at coroutine.

Ano ang Callback?

Callback (function ng tawag pabalik) — ay executable na code na ipinapasa sa ibang function at tinatawag pagkatapos makumpleto ang isang partikular na aksyon. Sa mobile development, ang callback ay ang pangunahing mekanismo ng asynchronous programming, na nagbibigay-daan sa pagtugon sa pagkumpleto ng mga network request, timer, animation, at input/output operations nang hindi hinaharangan ang main thread. Ang Swift at Kotlin ay nagbibigay ng mga built-in na syntactic construction para sa paglikha ng callback — mga closure at lambda ayon sa pagkakabanggit.

Prinsipyo ng Paggawa ng Callback

Ang higher-order function ay tumatanggap ng ibang function bilang parameter at tinatawag ito pagkatapos isagawa ang pangunahing lohika nito. Ang daloy ng kontrol ay bumabalik sa caller sa pamamagitan ng callback, kaya ang pangalan. Sa iOS, ang callback ay inilalapat sa UIKit (mga animation ng UIView.animate), Foundation (URLSession.dataTask), at Combine (sink). Sa Android, ang callback ay ginagamit sa View.OnClickListener, Retrofit Callback, at Room DAO. Ang mga modernong API ay lalong pinapalitan ang callback ng async/await o coroutine, ngunit ang pag-unawa sa callback ay kinakailangan para sa pagtatrabaho sa legacy code at low-level na API.

Synchronous at Asynchronous na Callback

Ang callback ay maaaring synchronous (tinatawag kaagad sa loob ng function) at asynchronous (tinatawag mamaya mula sa ibang thread o queue). Ang synchronous callback ay ginagamit para sa pag-uuri (mga comparator) at pagtawid sa mga koleksyon. Asynchronous callback ay inilalapat para sa mga network request, pagbabasa ng file, at pagtatrabaho sa mga sensor. Ang pagkakaiba ay kritikal para sa pag-unawa sa threading: ang synchronous callback ay isinasagawa sa parehong thread, ang asynchronous — sa thread na tinutukoy ng dispatcher (DispatchQueue sa iOS, Dispatchers sa Kotlin).

Paano gumagana ang Callback sa iOS at Android?

Ang mekanismo ng callback sa parehong platform ay batay sa parehong prinsipyo: ang function ay ipinapasa bilang first-class object at iniimbak hanggang sa oras ng pagpapatupad. Gayunpaman, ang mga implementasyon ay naiiba dahil sa iba't ibang paradigma ng wika. Sa iOS, ang callback ay isang closure na kumukuha ng mga variable mula sa nakapaligid na konteksto. Sa Android, ang callback ay kadalasang ipinapatupad sa pamamagitan ng mga anonymous class o Kotlin lambda expression, na na-compile sa FunctionalInterface.

Lifecycle ng Callback sa iOS

Kapag tinawag ang isang asynchronous function, ang closure ay iniimbak sa heap kasama ang mga nakuhang variable. Kapag nakumpleto ang operasyon, ang GCD o OperationQueue system ay naglalagay ng callback sa naaangkop na queue (main queue o background queue). Pagkatapos ng pagpapatupad, ang callback ay tinatanggal mula sa memory kung walang malakas na reference. Capture list ([weak self]) ay pumipigil sa pagpapanatili ng object pagkatapos ng deallocation. Kung walang capture list, magkakaroon ng retain cycle kung saan ang object at callback ay nagre-reference sa isa't isa.

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

// Paggamit na may [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)
    }
}

Lifecycle ng Callback sa Android

Sa Android, ang callback ay ipinapasa sa pamamagitan ng interface o lambda. Kapag nagsasagawa ng asynchronous operation sa pamamagitan ng ExecutorService o coroutine, ang callback ay iniimbak sa memory hanggang sa makumpleto ang background work. Kotlin lambda ay na-compile sa mga anonymous class na kumukuha ng mga panlabas na variable. Ang kawalan ng mahinang reference sa JVM ay nangangailangan ng manu-manong pamamahala: pag-reset ng callback sa onDestroy() o pagkansela ng coroutine sa pamamagitan ng Job.cancel(). Ang ViewModel at LiveData ay nilulutas ang problemang ito sa antas ng architectural component.

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

// Paggamit na may lambda
repository.loadData(object : Callback<List<User>> {
    override fun onSuccess(data: List<User>) { showUsers(data) }
    override fun onError(error: Throwable) { showError(error.message) }
})

Syntax ng Callback sa Swift at Kotlin

Ang syntax ng callback ay tinutukoy ng mga kakayahan ng wika sa pagtatrabaho sa mga function bilang first-class object. Sa Swift, ang mga closure ay may maikling syntax na may mga awtomatikong pangalan ng argumento ($0, $1). Sa Kotlin, ang lambda ay sumusuporta rin sa it para sa iisang argumento. Ang mga pagkakaiba ay lumalabas sa paghawak ng pagkuha ng variable (capture list sa Swift vs mutable reference sa Kotlin) at tipifikasi (Result<Success, Failure> vs Result<T>).

Callback sa Swift: mga closure

Ang Swift closure ay isang self-contained block ng code na maaaring ipasa at gamitin sa ibang function. Ang mga closure ay maaaring global (pinangalanan), nested, at expression-level. @escaping ay nagmamarka ng closure na isasagawa pagkatapos bumalik mula sa function — ito ay isang mandatoryong kinakailangan para sa asynchronous callback. Kung walang @escaping, ang closure ay maaari lamang isagawa sa loob ng body ng function. Ang trailing closure syntax ay nagpapahintulot sa pagpasa ng closure pagkatapos ng mga round bracket: 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 sa Kotlin: lambda at higher-order function

Ang Kotlin ay sumusuporta sa higher-order function na tumatanggap ng ibang function bilang parameter. Callback sa Kotlin ay ipinapasa sa pamamagitan ng parameter ng uri (T) -> Unit o (T) -> R para sa return value. Ang suspend function ng Kotlin coroutine ay pinapalitan ang callback ng sequential code, ngunit ang callback ay nananatili sa Java-compatible API at Android SDK (View.setOnClickListener, TextWatcher). Ang Kotlin lambda ay awtomatikong kumukuha ng val variable, ang var variable ay nangangailangan ng mutable wrapper.

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

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

Retain cycles at pagtagas ng memory sa Callback

Retain cycle — sitwasyon kung saan ang dalawang object ay nagpapanatili ng malakas na reference sa isa't isa, na pumipigil sa kanilang pagpapalaya ng garbage collector. Sa Swift, ang retain cycle ay nangyayari kapag ang viewController ay kumukuha ng closure, at ang closure ay kumukuha ng self. Sa Kotlin/Java, ang pagtagas ay nangyayari kapag ang Activity ay nagpasa ng inner class o lambda sa isang mahabang background operation. Ayon sa WWDC Session 10216 (2024), ang hindi wastong pamamahala ng mga closure ay ang ikatlong pinakakaraniwang sanhi ng pagtagas ng memory sa iOS application.

Retain cycles sa Swift

Ang Swift ay gumagamit ng Automatic Reference Counting (ARC), na nagpapalaya sa object kapag ang reference counter ay na-zero. Capture list [weak self] o [unowned self] sa closure ay pumipigil sa retain cycle. Ang weak self ay lumilikha ng opsyonal na reference na nagiging nil sa deallocation ng object. Ang unowned self ay nag-aakala na ang object ay nabubuhay nang mas matagal kaysa sa closure — ang paglabag sa palagay na ito ay nagdudulot ng crash. Ang paggamit ng weak self bilang ligtas na default na opsyon ay inirerekomenda.

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

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

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

    func process(_ input: String) { }
}

Pagtagas ng memory sa Android

Sa Android, ang pagtagas ng callback ay nangyayari kapag ang Activity o Fragment ay nagpasa ng listener sa isang singleton component (hal. EventBus o Service). WeakReference ay nagpapahintulot sa garbage collector na palayain ang Activity, kahit na may mahinang reference dito. Ang mga Lifecycle-aware component (LiveData, Flow) ay awtomatikong nilulutas ang problema. Ang Kotlin lambda na kumukuha ng Activity context ay maaari ring magdulot ng pagtagas: ang lambda ay implicit na nag-iimbak ng reference sa 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()
        }
    }
}

// Paggamit sa Fragment
manager.addListener { result ->
    // WeakReference ay hindi pinapanatili ang Fragment
    updateUI(result)
}

Callback Hell at mga paraan ng paglaban

Callback Hell (kilala rin bilang Pyramid of Doom) — sitwasyon kung saan maraming nested callback ang lumilikha ng malalim na nested code structure na mahirap basahin at i-debug. Ang bawat susunod na hakbang ay nangangailangan ng paghihintay sa pagkumpleto ng nauna, na humahantong sa 5-10 antas ng nesting. Ang problemang ito ay katangian ng sequential asynchronous operations: pag-load ng data → parsing → pag-save sa database → pag-update ng UI.

Mga solusyon sa Swift: async/await

Ang Swift 5.5 ay nagpakilala ng mga asynchronous function (async/await) na nagpapahintulot sa pagsulat ng asynchronous code nang sequential. AsyncSequence at AsyncStream ay pinapalitan ang callback-based iterations. Ang Combine framework ay nagbibigay ng mga operator na flatMap, merge, combineLatest para sa komposisyon ng asynchronous streams nang walang nesting. Gayunpaman, ang callback ay nananatiling kinakailangan para sa pagtatrabaho sa Objective-C API at third-party libraries na walang async support.

swift
// Nested 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 — solusyon
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)
}

Mga solusyon sa Kotlin: coroutine at Flow

Ang Kotlin coroutine ay pinapalitan ang callback ng suspend function na may sequential execution. Flow ay nagbibigay ng cold streams na may mga operator na map, flatMapConcat, combine. Ang CoroutineScope ay nagpapahintulot sa pagkansela ng lahat ng inilunsad na coroutine kapag nawasak ang component. Ang Room, Retrofit, at iba pang Jetpack library ay may built-in na suporta para sa suspend function, na nag-aalis ng pangangailangan para sa callback para sa standard operations.

kotlin
// Sequential 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()
            }
        }
    }
}

// Coroutine — solusyon
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: ano ang pipiliin?

Callback at Delegate — dalawang approach sa asynchronous notification, at ang pagpili sa pagitan ng mga ito ay depende sa architectural requirements. Ang callback ay angkop para sa isang beses na operasyon na may isang resulta. Ang delegate ay dinisenyo para sa maramihang mga event na may iba't ibang signature ng method. Inirerekomenda ng Apple ang delegate para sa kumplikadong protocol na may maraming method, callback — para sa simpleng closure na may isang resulta. Sa Android, pinapalitan ng callback ang delegate sa karamihan ng mga kaso dahil sa suporta ng lambda.

Kailan pumili ng Callback

Ang callback ay optimal para sa mga operasyon na may iisang resulta: network request, pagbabasa ng file, animation na may completion block. Mga Bentahe: kompaktong syntax, walang hiwalay na protocol, direktang pagkuha ng konteksto. Mga Disadvantages: komplikasyon sa maraming resulta (progreso, pause, pagkansela), imposibilidad ng maramihang pagpapadala (kung ang callback ay maaaring tawagin nang higit sa isang beses — gumamit ng publisher).

Kailan pumili ng Delegate

Ang delegate ay angkop para sa mga protocol na may maraming mandatory at optional method: UITableViewDelegate, CLLocationManagerDelegate, Bluetooth connection. Mga Bentahe: malinaw na tipifikasi ng bawat method, dokumentasyon sa pamamagitan ng protocol, suporta para sa optional method sa pamamagitan ng @objc optional. Mga Disadvantages: boilerplate code, mandatoryong mahinang reference sa delegate (weak var delegate), komplikasyon sa pagkuha ng konteksto.

Mga Madalas Itanong

Ano ang pagkakaiba ng callback at higher-order function?

Callback ay isang espesyal na kaso ng higher-order function. Ang higher-order function ay tumatanggap ng ibang function bilang argumento o ibinabalik ito. Ang callback ay isang function na espesyal na ipinapasa para sa asynchronous execution pagkatapos makumpleto ang operasyon. Lahat ng callback ay ipinapatupad sa pamamagitan ng higher-order function, ngunit hindi lahat ng higher-order function ay callback.

Maaari bang tawagin nang maraming beses ang callback?

Ayon sa convention, ang callback ay dapat tawagin nang eksaktong isang beses — alinman sa success o failure. Maramihang pagtawag ng parehong callback ay itinuturing na error sa disenyo. Para sa maramihang mga event (progreso, daloy ng data) gumamit ng Observable, Publisher o Flow — sinusuportahan nila ang maramihang paglabas ng halaga. Ang ilang API ay lumalabag sa panuntunang ito, na humahantong sa mahirap matukoy na bug.

Ano ang trailing closure sa Swift?

Trailing closure — syntactic sugar ng Swift na nagpapahintulot sa pagpasa ng closure pagkatapos ng mga round bracket ng function call. Kung ang function ay tumatanggap ng closure bilang huling argumento, maaari itong ilagay sa labas ng bracket: fetchData { result in ... }. Para sa maramihang closure, ang trailing closure ay inilalapat lamang sa huli, ang iba ay pinangalanan sa loob ng bracket. Pinapabuti nito ang pagiging nababasa ng callback-based na API.

Paano maiwasan ang pagtagas ng memory sa callback sa Android?

Gumamit ng WeakReference para sa mahabang buhay na listener, kanselahin ang coroutine sa pamamagitan ng Job.cancel() sa onDestroy(), ilapat ang lifecycleScope para sa awtomatikong pagkansela. Ang ViewModel + LiveData/Flow ay nilulutas ang problema sa antas ng arkitektura. Iwasan ang pagpasa ng Activity context sa static callback — gumamit ng Application context. Ang Kotlin lambda ay implicit na kumukuha ng this, suriin sa pamamagitan ng memory profiler.

Papalitan ba ng async/await ang callback nang buo?

Async/await ay pinapalitan ang callback para sa sequential asynchronous code, ngunit hindi para sa event-driven architecture. Ang callback ay nananatili sa system API (View.OnClickListener, URLSession delegate), callback na may progreso, at third-party library. Ang buong pagpapalit ay imposible dahil sa backward compatibility. Ang modernong diskarte ay ang paggamit ng async/await na may callback wrapper (continuation sa Swift, suspendCancellableCoroutine sa Kotlin).

Buod

  • Callback — function ng tawag pabalik na ipinapasa bilang argumento para sa asynchronous execution pagkatapos makumpleto ang operasyon.
  • Swift ay nagpapatupad ng callback sa pamamagitan ng closure na may @escaping, capture list [weak self], at trailing closure syntax.
  • Kotlin ay gumagamit ng lambda, higher-order function, at suspend function ng coroutine para sa asynchrony.
  • Retain cycle sa iOS ay pinipigilan ng capture list; sa Android — ng WeakReference at Lifecycle-aware component.
  • Callback Hell ay nalulutas sa pamamagitan ng async/await sa Swift at coroutine na may Flow sa Kotlin.
  • Delegate ay mas gusto kaysa callback para sa mga protocol na may maraming method; callback — para sa isang beses na operasyon.
  • Gumamit ng callback para sa simpleng asynchronous operations, async/await para sa sequential chains, delegate para sa maramihang event.

Gagawa kami ng mobile application na turnkey

Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.

Pag-usapan ang proyekto

Basahin din