Published on

Swiftda ARC chuqur — xotira boshqaruvi

Authors

ARC — Automatic Reference Counting

ARC — Swift ning xotira boshqarish tizimi. U class (reference type) ob'ektlarini kuzatadi va ularga nechta reference borligini sanaydi. Reference soni 0 ga tushganda ob'ekt xotiradan avtomatik bo'shatiladi. Bu garbage collector emas — ARC compile-time da ishlaydi va hech qanday to'xtash (pause) yaratmaydi.

Lekin ARC ham xato qilish mumkin — ayniqsa class, closure va delegate ishlatganingizda retain cycle (xotira leak) yaratishingiz mumkin. Bu darsda ARC qanday ishlashini va xotira leak oldini olishni o'rganasiz.

ARC ishlash printsipi

Har safar class ob'ektiga yangi reference yaratilganda (o'zgaruvchi, property, closure) reference count oshadi. Reference yo'qolganda (scope dan chiqish, nil ga o'rnatish) kamayadi. Count 0 ga tushganda deinit chaqiriladi va ob'ekt xotiradan o'chiriladi.

// ═══════════════════════════════════════
//  ARC — reference counting
// ═══════════════════════════════════════
class Shaxs {
    let ism: String
    init(ism: String) {
        self.ism = ism
        print("\(ism) yaratildi")
    }
    deinit {
        print("\(ism) xotiradan chiqdi")
    }
}

// Reference count o'zgarishi:
var ref1: Shaxs? = Shaxs(ism: "Ali")  // RC = 1 (yaratildi)
var ref2 = ref1                         // RC = 2
var ref3 = ref1                         // RC = 3

ref1 = nil  // RC = 2 — hali xotiradan chiqmaydi
ref2 = nil  // RC = 1 — hali xotiradan chiqmaydi
ref3 = nil  // RC = 0 — "Ali xotiradan chiqdi" ✅

// Diagramma:
// ref1 ──┐
// ref2 ──┼──► Shaxs("Ali")  RC = 3
// ref3 ──┘
//
// ref1=nil ──┐
// ref2 ──────┼──► Shaxs("Ali")  RC = 2
// ref3 ──────┘
//
// ... RC = 0 → deinit → xotiradan bo'shatildi

Retain Cycle — xotira leak

Retain cycle — ikki ob'ekt bir-birini strong reference bilan ushlaydi va hech biri xotiradan chiqmaydi. deinit hech qachon chaqirilmaydi. Bu xotira leak — vaqt o'tishi bilan ilova ko'p xotira iste'mol qiladi va crash bo'lishi mumkin. Yechim — bir tomonni weak qilish.

// ═══════════════════════════════════════
//  ❌ RETAIN CYCLE
// ═══════════════════════════════════════
class Xona {
    let raqam: Int
    var mijoz: Mijoz?    // strong reference

    init(raqam: Int) { self.raqam = raqam }
    deinit { print("Xona \(raqam) bo'shatildi") }
}

class Mijoz {
    let ism: String
    var xona: Xona?      // ❌ strong reference — retain cycle!

    init(ism: String) { self.ism = ism }
    deinit { print("\(ism) ketdi") }
}

var xona: Xona? = Xona(raqam: 101)
var mijoz: Mijoz? = Mijoz(ism: "Ali")

xona?.mijoz = mijoz  // Xona → Mijoz (strong)
mijoz?.xona = xona   // Mijoz → Xona (strong) — RETAIN CYCLE!

xona = nil   // ❌ deinit CHAQIRILMAYDI — Mijoz hali ushlayapti
mijoz = nil  // ❌ deinit CHAQIRILMAYDI — Xona hali ushlayapti
// Ikkalasi ham xotiradan CHIQMADI — LEAK!


// ═══════════════════════════════════════
//  ✅ WEAK BILAN TUZATISH
// ═══════════════════════════════════════
class XonaTuzatilgan {
    let raqam: Int
    var mijoz: MijozTuzatilgan?

    init(raqam: Int) { self.raqam = raqam }
    deinit { print("Xona \(raqam) bo'shatildi") }
}

class MijozTuzatilgan {
    let ism: String
    weak var xona: XonaTuzatilgan?  // ✅ WEAK — retain cycle yo'q

    init(ism: String) { self.ism = ism }
    deinit { print("\(ism) ketdi") }
}

var x: XonaTuzatilgan? = XonaTuzatilgan(raqam: 101)
var m: MijozTuzatilgan? = MijozTuzatilgan(ism: "Ali")

x?.mijoz = m   // Xona → Mijoz (strong)
m?.xona = x    // Mijoz → Xona (WEAK) — cycle yo'q

x = nil  // "Xona 101 bo'shatildi" ✅
m = nil  // "Ali ketdi" ✅

weak vs unowned

weak — reference count ni oshirmaydi. Ob'ekt bo'shatilsa weak reference avtomatik nil ga aylanadi. Shuning uchun weak har doim Optional (var va ?). Ob'ekt yo'q bo'lishi mumkin bo'lgan holatlarda ishlatiladi.

unowned — ham reference count ni oshirmaydi, lekin Optional emas. Agar ob'ekt bo'shatilgan bo'lsa va unowned reference ga murojaat qilinsa — crash! Faqat ob'ekt albatta tirik ekaniga ishonchingiz bo'lganda ishlatiladi (masalan, kredit karta — egasisiz mavjud emas).

// ═══════════════════════════════════════
//  WEAK — nil bo'lishi mumkin (Optional)
// ═══════════════════════════════════════
class Mashina {
    let model: String
    weak var egasi: Shaxs?  // Egasi yo'q bo'lishi mumkin

    init(model: String) { self.model = model }
}

// ═══════════════════════════════════════
//  UNOWNED — nil bo'lishi MUMKIN EMAS
//  Ob'ekt albatta tirik deb ishonasiz
//  Agar yo'q bo'lsa — CRASH!
// ═══════════════════════════════════════
class KreditKarta {
    let raqam: Int
    unowned let egasi: Shaxs  // Egasi albatta bor — kartasiz ega yo'q

    init(raqam: Int, egasi: Shaxs) {
        self.raqam = raqam
        self.egasi = egasi
    }
}

// unowned — ob'ekt albatta boshqa ob'ektdan uzoq yashaganda
// KreditKarta egasisiz mavjud bo'lmaydi — shuning uchun unowned

Closure da retain cycle

@escaping closure self ni strong capture qilganda va self ham closure ni strong saqlasa — retain cycle paydo bo'ladi. Yechim: [weak self] yoki [unowned self] capture list ishlatish. [weak self] xavfsizroq — self yo'q bo'lsa nil, guard let self bilan tekshirish mumkin. [unowned self]self albatta tirik deb ishonasiz, aks holda crash.

// ═══════════════════════════════════════
//  CLOSURE RETAIN CYCLE VA YECHIMLAR
// ═══════════════════════════════════════
class TarmoqMenejer {
    var natija = ""
    var callback: (() -> Void)?

    // ❌ Retain cycle
    func xatoUsul() {
        callback = {
            self.natija = "Yuklandi"  // self ni strong capture
        }
        // self → callback → self → ... ♻️
    }

    // ✅ [weak self] bilan tuzatish
    func togriUsul() {
        callback = { [weak self] in
            guard let self else { return }
            self.natija = "Yuklandi"
        }
    }

    // ✅ [unowned self] — self albatta tirik
    func unownedUsul() {
        callback = { [unowned self] in
            self.natija = "Yuklandi"  // Agar self yo'q bo'lsa — crash!
        }
    }

    deinit { print("Menejer xotiradan chiqdi") }
}

Xcode da xotira leak topish

Xcode da xotira leak topish uchun ikkita kuchli vosita bor: Instruments (Leaks va Allocations) — ilovani profil qilib, xotira leak larni real-time aniqlaydi. Memory Graph Debugger — ob'ektlar orasidagi bog'lanishlarni vizual ko'rsatadi, retain cycle ni ko'rish oson.

InstrumentsLeaks:
1. XcodeProductProfile (I)
2. "Leaks" ni tanlang
3. Ilovani ishlatib ko'ring
4. Qizil ⚠️ — leak topildi

Memory Graph Debugger:
1. Ilova ishlayotganda Debug NavigatorMemory
2. Pastdagi "Debug Memory Graph" tugmasi
3. Ob'ektlar orasidagi bog'lanishlarni ko'ring
4. Aylanma (cycle) ko'rsangiz — retain cycle

🎯 Topshiriq

Professor va Talaba classlari yarating — professor talabalar ro'yxatini, talaba professorni saqlsin. Avval ikkalasini strong qiling va deinit chaqirilmasligini ko'ring (retain cycle). Keyin weak bilan tuzating va deinit ishlashini tasdiqlang.

Buy mea coffee