Published on

Swiftda @escaping Closures va Capture List

Authors

Closure β€” funksiya ichida yaratilgan va tashqi o'zgaruvchilarni "ushlab oladigan" (capture) kod bloki. Lekin closure tashqi ob'ektlarni ushlaganda xotira muammolari paydo bo'lishi mumkin. Bu darsda closurelarning xotira bilan munosabatini o'rganamiz.

Escaping va Non-escaping farqi

// ═══════════════════════════════════════
//  NON-ESCAPING β€” funksiya ichida chaqiriladi va tugaydi
//  Swift da closure parametrlari default non-escaping
// ═══════════════════════════════════════
func hisoblash(amal: () -> Void) {
    // Closure shu yerda β€” funksiya ichida chaqiriladi
    amal()
    // Funksiya tugaganda closure ham yo'q bo'ladi
}

hisoblash {
    print("Bu non-escaping closure")
}


// ═══════════════════════════════════════
//  @ESCAPING β€” funksiyadan "qochadi", keyinroq chaqiriladi
//  Asinxron operatsiyalar, callback lar uchun
// ═══════════════════════════════════════
class TarmoqMenejer {
    // completionHandler β€” tarmoq so'rovi tugaganda chaqiriladi
    // Funksiya allaqachon qaytgan bo'ladi β€” shuning uchun @escaping
    func ma'lumotYukla(completion: @escaping (String) -> Void) {
        // Tarmoq so'rovi β€” vaqt oladi
        DispatchQueue.global().async {
            // ... tarmoqdan ma'lumot keldi
            let natija = "Salom, Server!"

            // Funksiya allaqachon qaytgan, lekin closure hali chaqirilmoqda
            DispatchQueue.main.async {
                completion(natija)
            }
        }
    }
}

let menejer = TarmoqMenejer()
menejer.ma'lumotYukla { natija in
    print(natija) // "Salom, Server!" β€” keyinroq chiqadi
}


// ═══════════════════════════════════════
//  @ESCAPING β€” property da saqlash
// ═══════════════════════════════════════
class TugmaViewModel {
    // Closure property da saqlanadi β€” funksiyadan qochadi
    var bosilganAction: (() -> Void)?

    func sozlash(action: @escaping () -> Void) {
        // Closure property ga saqlanmoqda β€” @escaping kerak
        self.bosilganAction = action
    }

    func tugmaBosildi() {
        bosilganAction?()
    }
}

Capture va Retain Cycle

// ═══════════════════════════════════════
//  RETAIN CYCLE MUAMMOSI
//  Closure self ni ushlaydi, self closure ni ushlaydi
//  Hech biri xotiradan bo'shatilmaydi!
// ═══════════════════════════════════════
class ProfilViewModel {
    var ism = "Ali"
    var yuklashTugadi: (() -> Void)?

    func yuklash() {
        // ❌ XATO β€” retain cycle!
        // self β†’ yuklashTugadi (closure ni ushlaydi)
        // yuklashTugadi β†’ self (self ni ushlaydi)
        // Natija: ikkalasi ham xotiradan chiqmaydi
        yuklashTugadi = {
            print("Salom, \(self.ism)!")  // self ni strong ushlaydi
        }
    }

    deinit {
        print("\(ism) xotiradan chiqdi") // ❌ Hech qachon chaqirilmaydi!
    }
}


// ═══════════════════════════════════════
//  [weak self] BILAN YECHIM
// ═══════════════════════════════════════
class ProfilViewModelTuzatilgan {
    var ism = "Ali"
    var yuklashTugadi: (() -> Void)?

    func yuklash() {
        // βœ… TO'G'RI β€” [weak self] retain cycle ni buzadi
        // self β†’ yuklashTugadi (closure ni ushlaydi)
        // yuklashTugadi β†’ self ni WEAK ushlaydi (retain cycle yo'q)
        yuklashTugadi = { [weak self] in
            // self endi Optional β€” nil bo'lishi mumkin
            guard let self else { return }
            print("Salom, \(self.ism)!")
        }
    }

    deinit {
        print("\(ism) xotiradan chiqdi") // βœ… Chaqiriladi!
    }
}

[weak self] vs [unowned self]

class YuklovchiView {
    var sarlavha = "Yuklanmoqda..."

    func yuklash() {
        // ═══════════════════════════════════════
        //  [weak self] β€” XAVFSIZ
        //  self nil bo'lishi MUMKIN
        //  Optional sifatida ishlatiladi
        // ═══════════════════════════════════════
        tarmoqSorov { [weak self] natija in
            // self yo'q bo'lishi mumkin (ekran yopilgan)
            guard let self else { return }
            self.sarlavha = natija
        }

        // ═══════════════════════════════════════
        //  [unowned self] β€” TEZROQ lekin XAVFLI
        //  self hech qachon nil bo'lmasligiga ISHONASIZ
        //  Agar nil bo'lsa β€” CRASH!
        // ═══════════════════════════════════════
        // Faqat self albatta tirik bo'lishiga 100% ishonchingiz bo'lsa
        tarmoqSorov { [unowned self] natija in
            // Agar self yo'q bo'lsa β€” crash!
            self.sarlavha = natija
        }
    }

    func tarmoqSorov(completion: @escaping (String) -> Void) {
        DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
            completion("Tayyor!")
        }
    }
}

Qachon nima ishlatish kerak

HolatIshlatishSabab
Tarmoq so'rovi callback[weak self]Ekran yopilishi mumkin
Timer callback[weak self]View yo'q bo'lishi mumkin
Animatsiya completionHech nima kerak emasUIKit o'zi boshqaradi
Oddiy map, filterHech nima kerak emasNon-escaping β€” muammo yo'q
Self albatta tirik[unowned self]Faqat 100% ishonch bilan

Capture list bilan bir nechta qiymat ushlash

var x = 10
var y = 20

// Capture list β€” tashqi qiymatlarni "surat oladi"
let closure = { [x, y] in
    // x va y closure yaratilgan paytdagi qiymatlari
    print("x = \(x), y = \(y)")  // x = 10, y = 20
}

x = 100  // Tashqarida o'zgardi
y = 200

closure()  // x = 10, y = 20 β€” eski qiymatlar!
// Chunki capture list NUSXA olgan (value type uchun)

🎯 Topshiriq

Quyidagi kodni tuzating β€” retain cycle ni toping va [weak self] bilan hal qiling:

class Taymer {
    var vaqt = 0
    var timer: Timer?

    func boshlash() {
        timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
            self.vaqt += 1
            print("Vaqt: \(self.vaqt)")
        }
    }

    deinit { print("Taymer xotiradan chiqdi") }
}
Buy mea coffee