- Published on
Swiftda @escaping closures va capture list
- Authors
- Name
- ShoxruxC
- @iOSdasturchi
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
// Swiftda 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
| Holat | Ishlatish | Sabab |
|---|---|---|
| Tarmoq so'rovi callback | [weak self] | Ekran yopilishi mumkin |
| Timer callback | [weak self] | View yo'q bo'lishi mumkin |
| Animatsiya completion | Hech nima kerak emas | UIKit o'zi boshqaradi |
Oddiy map, filter | Hech nima kerak emas | Non-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") }
}