Callback — bu nədir, geri çağırış funksiyaları və necə işləyir

Müəllif: IT Sectr Dərc olunub: 2026-03-17 Oxuma vaxtı: 11 dəq

Callback — bu, asinxron əməliyyat tamamlandıqdan sonra yerinə yetirilmək üçün başqa bir funksiyaya arqument kimi ötürülən funksiyadır. Mobil inkişafda callback şəbəkə sorğularının nəticələrinin işlənməsi, verilənlər bazaları ilə iş və animasiyalar üçün tətbiq olunur. Apple Documentation (2025) məlumatına görə, Swift-də closures callback-in əsas formasıdır və URLSession, GCD və Combine-də istifadə olunur. Android-də callback interfeyslər, Kotlin lambda ifadələri və ListenableFuture vasitəsilə həyata keçirilir.

Əsas məqamlar

  • Callback — asinxron icra üçün arqument kimi ötürülən geri çağırış funksiyası.
  • Swift callback üçün @escaping açar sözü ilə closures istifadə edir.
  • Kotlin callback üçün lambda ifadələri və yüksək səviyyəli funksiyalar tətbiq edir.
  • Retain cycle — iOS-da callback-də self-in tutulması zamanı yaddaş sızması.
  • Callback Hell — async/await və korutinlər vasitəsilə həll edilən iç-içə callback problemi.

Callback nədir?

Callback (geri çağırış funksiyası) — bu, başqa bir funksiyaya ötürülən və müəyyən bir hərəkət tamamlandıqdan sonra çağrılan icra olunan koddur. Mobil inkişafda callback asinxron proqramlaşdırmanın əsas mexanizmidir və əsas axını bloklamadan şəbəkə sorğularının, taymerlərin, animasiyaların və giriş-çıxış əməliyyatlarının tamamlanmasına reaksiya verməyə imkan verir. Swift və Kotlin callback yaratmaq üçün daxili sintaktik konstruksiyalar təqdim edir — müvafiq olaraq closures və lambdalar.

Callback-in iş prinsipi

Yüksək səviyyəli funksiya başqa bir funksiyanı parametr kimi qəbul edir və öz əsas məntiqini yerinə yetirdikdən sonra onu çağırır. İdarəetmə axını callback vasitəsilə zəng edənə qaytarılır, adı da buradan gəlir. iOS-da callback UIKit (UIView.animate animasiyaları), Foundation (URLSession.dataTask) və Combine (sink) tətbiq olunur. Android-də callback View.OnClickListener, Retrofit Callback və Room DAO-da istifadə olunur. Müasir API-lər getdikcə callback-i async/await və ya korutinlərlə əvəz edir, lakin callback-i başa düşmək legacy kod və aşağı səviyyəli API-lərlə iş üçün zəruridir.

Senkron və asinxron Callback

Callback senkron (funksiya daxilində dərhal çağrılan) və asinxron (sonradan başqa bir axından və ya növbədən çağrılan) ola bilər. Senkron callback-lər sıralama (komparatorlar) və kolleksiyaları gəzmək üçün istifadə olunur. Asinxron callback-lər şəbəkə sorğuları, faylların oxunması və sensorlarla iş üçün tətbiq olunur. Fərq threading-i başa düşmək üçün kritikdir: senkron callback eyni axında icra olunur, asinxron — dispetçer tərəfindən müəyyən edilmiş axında (iOS-da DispatchQueue, Kotlin-də Dispatchers).

Callback iOS və Android-də necə işləyir?

Hər iki platformada callback mexanizmi eyni prinsipə əsaslanır: funksiya birinci dərəcəli obyekt kimi ötürülür və icra anına qədər saxlanılır. Lakin tətbiqlər fərqli dil paradiqmalarına görə dəyişir. iOS-da callback — ətraf kontekstdən dəyişənləri tutan closure-dır. Android-də callback ən çox anonim siniflər və ya FunctionalInterface-ə kompilyasiya olunan Kotlin lambda ifadələri vasitəsilə həyata keçirilir.

iOS-da Callback-in həyat dövrü

Asinxron funksiya çağırıldıqda, closure tutulmuş dəyişənlərlə birlikdə yığında saxlanılır. Əməliyyat tamamlandıqda, GCD və ya OperationQueue sistemi callback-i müvafiq növbəyə (main queue və ya background queue) yerləşdirir. İcradan sonra güclü istinadlar olmadıqda callback yaddaşdan silinir. Capture list ([weak self]) obyektin dealokasiyadan sonra saxlanılmasının qarşısını alır. Capture list olmadan retain cycle yaranır, burada obyekt və callback bir-birinə istinad edir.

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

// [weak self] ilə istifadə
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)
    }
}

Android-də Callback-in həyat dövrü

Android-də callback interfeys və ya lambda vasitəsilə ötürülür. ExecutorService və ya korutin vasitəsilə asinxron əməliyyat yerinə yetirilərkən, callback fon işi tamamlanana qədər yaddaşda saxlanılır. Kotlin lambdaları xarici dəyişənləri tutan anonim siniflərə kompilyasiya olunur. JVM-də zəif istinadların olmaması əl ilə idarəetmə tələb edir: onDestroy()-də callback-in sıfırlanması və ya Job.cancel() vasitəsilə korutinlərin ləğvi. ViewModel və LiveData bu problemi memarlıq komponenti səviyyəsində həll edir.

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

// Lambda ilə istifadə
repository.loadData(object : Callback<List<User>> {
    override fun onSuccess(data: List<User>) { showUsers(data) }
    override fun onError(error: Throwable) { showError(error.message) }
})

Swift və Kotlin-də Callback sintaksisi

Callback sintaksisi funksiyalarla birinci dərəcəli obyektlər kimi işləmək üçün dil imkanları ilə müəyyən edilir. Swift-də closures avtomatik arqument adları ($0, $1) ilə qısa sintaksisə malikdir. Kotlin-də lambdalar da tək arqument üçün it dəstəkləyir. Fərqlər dəyişənlərin tutulması (Swift-də capture list vs Kotlin-də mutasiya olunan istinadlar) və tipizasiyada (Result<Success, Failure> vs Result<T>) özünü göstərir.

Swift-də Callback: closures

Swift closure-u öz-özünə kifayət edən kod blokudur ki, başqa funksiyaya ötürülə və istifadə oluna bilər. Closures qlobal (adlandırılmış), iç-içə və expression-level ola bilər. @escaping funksiyadan qayıtdıqdan sonra icra olunacaq closure-u qeyd edir — bu asinxron callback-lər üçün məcburi tələbdir. @escaping olmadan closure yalnız funksiyanın gövdəsi daxilində icra oluna bilər. Trailing closure syntax closure-u dairəvi mötərizələrdən sonra ötürməyə imkan verir: 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)
    }
}

Kotlin-də Callback: lambdalar və yüksək səviyyəli funksiyalar

Kotlin başqa funksiyaları parametr kimi qəbul edən yüksək səviyyəli funksiyaları dəstəkləyir. Callback Kotlin-də (T) -> Unit və ya qaytarılan dəyər üçün (T) -> R tipinin parametri vasitəsilə ötürülür. Kotlin korutinlərinin suspend funksiyaları callback-i ardıcıl kodla əvəz edir, lakin callback Java-uyğun API və Android SDK-da (View.setOnClickListener, TextWatcher) qalır. Kotlin lambdaları avtomatik olaraq val dəyişənlərini tutur, var dəyişənləri mutasiya olunan qablaşdırmalar tələb edir.

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

// Lambda ilə nümunə
processWithCallback(
    input = "Hello",
    transform = { it.length },
    onResult = { length ->
        textView.text = "Length: $length"
    }
)

Retain cycles və Callback-də yaddaş sızmaları

Retain cycle — iki obyektin bir-birinə güclü istinadlar saxlayaraq yaddaşın boşaldılmasının qarşısını aldığı vəziyyətdir. Swift-də retain cycle viewController closure-u tutduqda və closure self-i tutduqda yaranır. Kotlin/Java-da Activity daxili sinif və ya lambda uzunmüddətli fon əməliyyatına ötürdükdə sızma baş verir. WWDC Session 10216 (2024) məlumatına görə, closures-un düzgün idarə edilməməsi iOS tətbiqlərində yaddaş sızmalarının üçüncü ən çox yayılmış səbəbidir.

Swift-də Retain cycles

Swift istinad sayğacı sıfırlandıqda obyekti boşaldan Automatic Reference Counting (ARC) istifadə edir. Closure-da Capture list [weak self] və ya [unowned self] retain cycle-in qarşısını alır. weak self obyektin dealokasiyası zamanı nil olan opsional istinad yaradır. unowned self closure-dan daha uzun yaşadığını fərz edir — bu fərziyyə pozularsa, crash baş verir. Təhlükəsiz default variant kimi weak self istifadə etmək tövsiyə olunur.

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

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

        // Düzəldildi: [weak self]
        onDataUpdate = { [weak self] text in
            guard let self else { return }
            self.process(text)
        }
    }

    func process(_ input: String) { }
}

Android-də yaddaş sızmaları

Android-də callback sızması Activity və ya Fragment dinləyicini singleton komponentə (məsələn, EventBus və ya Service) ötürdükdə baş verir. WeakReference garbage collector-a zəif istinad olsa belə Activity-ni boşaltmağa imkan verir. Lifecycle-aware komponentləri (LiveData, Flow) problemi avtomatik həll edir. Activity kontekstini tutan Kotlin lambdaları da sızmaya səbəb ola bilər: lambda gizli şəkildə this-ə istinad saxlayır.

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

// Fragment-də istifadə
manager.addListener { result ->
    // WeakReference Fragment-i saxlamır
    updateUI(result)
}

Callback Hell və mübarizə üsulları

Callback Hell (həmçinin Fəlakət Piramidası kimi tanınır) — çoxsaylı iç-içə callback-lərin oxuması və debug edilməsi çətin olan dərin iç-içə kod strukturu yaratdığı vəziyyətdir. Hər növbəti addım əvvəlkinin tamamlanmasını gözləməyi tələb edir, bu da 5-10 səviyyəli iç-içəliyə gətirib çıxarır. Bu problem ardıcıl asinxron əməliyyatlar üçün xarakterikdir: məlumat yükləmə → pars etmə → verilənlər bazasına yazma → UI yeniləmə.

Swift-də həllər: async/await

Swift 5.5 asinxron kodu ardıcıl yazmağa imkan verən asinxron funksiyalar (async/await) təqdim etdi. AsyncSequenceAsyncStream callback-əsaslı iterasiyaları əvəz edir. Combine framework-ü iç-içə olmadan asinxron axınların kompozisiyası üçün flatMap, merge, combineLatest operatorlarını təqdim edir. Lakin callback Objective-C API və async dəstəyi olmayan üçüncü tərəf kitabxanaları ilə iş üçün zəruri olaraq qalır.

swift
// İç-içə 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 — həll
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)
}

Kotlin-də həllər: korutinlər və Flow

Kotlin korutinləri callback-i ardıcıl icra ilə suspend funksiyaları ilə əvəz edir. Flow map, flatMapConcat, combine operatorları ilə cold axınlar təqdim edir. CoroutineScope komponent məhv edildikdə bütün işə salınmış korutinləri ləğv etməyə imkan verir. Room, Retrofit və digər Jetpack kitabxanaları standart əməliyyatlar üçün callback ehtiyacını aradan qaldıran daxili suspend funksiya dəstəyinə malikdir.

kotlin
// Ardıcıl 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()
            }
        }
    }
}

// Korutinlər — həll
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: nə seçmək?

CallbackDelegate — asinxron bildiriş üçün iki yanaşmadır və onların arasında seçim memarlıq tələblərindən asılıdır. Callback tək nəticəli birdəfəlik əməliyyatlar üçün uyğundur. Delegate müxtəlif metod imzaları olan çoxsaylı hadisələr üçün nəzərdə tutulub. Apple çox metodlu mürəkkəb protokollar üçün delegate-i, tək nəticəli sadə closures üçün callback-i tövsiyə edir. Android-də callback lambda dəstəyinə görə əksər hallarda delegate-i əvəz edir.

Callback nə zaman seçilməlidir

Callback tək nəticəli əməliyyatlar üçün optimaldır: şəbəkə sorğusu, fayl oxuma, completion bloku ilə animasiya. Üstünlüklər: yığcam sintaksis, ayrıca protokolun olmaması, kontekstin birbaşa tutulması. Çatışmazlıqlar: çoxsaylı nəticələr zamanı mürəkkəblik (tərəqqi, fasilə, ləğv), çoxsaylı göndərmənin mümkünsüzlüyü (callback birdən çox çağrıla bilərsə — publisher istifadə edin).

Delegate nə zaman seçilməlidir

Delegate çoxsaylı məcburi və opsional metodları olan protokollar üçün uyğundur: UITableViewDelegate, CLLocationManagerDelegate, Bluetooth bağlantıları. Üstünlüklər: hər metodun aydın tipizasiyası, protokol vasitəsilə sənədləşmə, @objc optional ilə opsional metodların dəstəyi. Çatışmazlıqlar: boilerplate kod, delegate üçün məcburi zəif istinad (weak var delegate), kontekstin tutulmasında mürəkkəblik.

Tez-tez verilən suallar

Callback və yüksək səviyyəli funksiya arasında nə fərq var?

Callback yüksək səviyyəli funksiyanın xüsusi halıdır. Yüksək səviyyəli funksiya başqa bir funksiyanı arqument kimi qəbul edir və ya onu qaytarır. Callback əməliyyat tamamlandıqdan sonra asinxron icra üçün xüsusi olaraq ötürülən funksiyadır. Bütün callback-lər yüksək səviyyəli funksiyalar vasitəsilə həyata keçirilir, lakin hər yüksək səviyyəli funksiya callback deyil.

Callback bir neçə dəfə çağrıla bilərmi?

Konvensiyaya görə callback dəqiq bir dəfə çağrılmalıdır — ya success, ya da failure. Çoxsaylı çağrı eyni callback dizayn xətası hesab olunur. Çoxsaylı hadisələr üçün (tərəqqi, məlumat axını) Observable, Publisher və ya Flow istifadə edin — onlar çoxsaylı dəyər emissiyasını dəstəkləyir. Bəzi API-lər bu qaydanı pozur, bu da çətin aşkar edilən səhvlərə gətirib çıxarır.

Swift-də trailing closure nədir?

Trailing closure — Swift-in sintaktik şəkəridir, closure-u funksiya çağırışının dairəvi mötərizələrindən sonra ötürməyə imkan verir. Funksiya closure-u son arqument kimi qəbul edirsə, onu mötərizələrdən kənara çıxarmaq olar: fetchData { result in ... }. Bir neçə closure üçün trailing closure yalnız sonuncuya tətbiq olunur, qalanları mötərizələr daxilində adlandırılır. Bu, callback-əsaslı API-in oxunaqlılığını yaxşılaşdırır.

Android-də callback zamanı yaddaş sızmasından necə qaçınmaq olar?

Uzunömürlü dinləyicilər üçün WeakReference istifadə edin, onDestroy()-də Job.cancel() vasitəsilə korutinləri ləğv edin, avtomatik ləğv üçün lifecycleScope tətbiq edin. ViewModel + LiveData/Flow problemi memarlıq səviyyəsində həll edir. Statik callback-lərə Activity kontekstini ötürməkdən çəkinin — Application kontekstindən istifadə edin. Kotlin lambdaları gizli şəkildə this-i tutur, memory profiler vasitəsilə yoxlayın.

Async/await callback-i tamamilə əvəz edəcəkmi?

Async/await ardıcıl asinxron kod üçün callback-i əvəz edir, lakin event-driven memarlıq üçün yox. Callback sistem API-lərində (View.OnClickListener, URLSession delegatları), tərəqqi ilə geri çağırışlarda və üçüncü tərəf kitabxanalarında qalır. Tam əvəz etmə geriyə uyğunluq səbəbindən mümkün deyil. Müasir strategiya callback sarğıları ilə async/await istifadə etməkdir (Swift-də continuation, Kotlin-də suspendCancellableCoroutine).

Nəticə

  • Callback — əməliyyat tamamlandıqdan sonra asinxron icra üçün arqument kimi ötürülən geri çağırış funksiyası.
  • Swift callback-i @escaping, capture list [weak self] və trailing closure syntax ilə closures vasitəsilə həyata keçirir.
  • Kotlin asinxronluq üçün lambdalar, yüksək səviyyəli funksiyalar və korutinlərin suspend funksiyalarından istifadə edir.
  • Retain cycle iOS-da capture list ilə qarşısı alınır; Android-də — WeakReference və Lifecycle-aware komponentləri ilə.
  • Callback Hell Swift-də async/await və Kotlin-də Flow ilə korutinlər vasitəsilə həll edilir.
  • Delegate çox metodlu protokollar üçün callback-dən üstündür; callback — birdəfəlik əməliyyatlar üçün.
  • Sadə asinxron əməliyyatlar üçün callback, ardıcıl zəncirlər üçün async/await, çoxsaylı hadisələr üçün delegate istifadə edin.

Açar təslim mobil tətbiq hazırlayacağıq

IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.

Layihəni müzakirə et

Həm də oxuyun