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