Sobes.tech
Middle

Swift'da Avtomatik Xotira Boshqaruvi (ARC) qanday ishlaydi?

sobes.tech AI

AIdan javob

ARC (Avtomatik Reference Counting) Swiftda sizning ilovalaringizning xotira foydalanishini kuzatadi va boshqaradi. U avtomatik ravishda sinflar nusxalari uchun ishlatiladigan xotirani bo'shatadi, ular endi kerak emas, bu esa xotira oqib ketishining oldini oladi.

Yangi sinf nusxasi yaratilganda, ARC bu nusxa haqida ma'lumot saqlash uchun xotira blokini ajratadi. Nusxa endi kerak bo'lmaganda, ARC bu xotirani bo'shatadi, shuning uchun u boshqa maqsadlar uchun ishlatilishi mumkin.

ARC har bir sinf nusxasiga kuchli havolalar (strong references) sonini hisoblab boradi. Kuchli havola — bu nusxani xotirada ushlab turadigan havola. Nusxaga kuchli havolalar soni nolga teng bo'lsa, ARC unga bog'langan xotirani bo'shatadi.

Swiftda ARCga ta'sir qiladigan uchta turdagi havolalar mavjud:

  1. Kuchli havolalar (Strong References): Nusxaga havolalar sonini oshiradi.
  2. Kuchsiz havolalar (Weak References): Havolalar sonini oshirmaydi. Kuchli havolalar aylanishini oldini olish uchun ishlatiladi, ikki sinf nusxasi bir-biriga kuchli havolalar bilan bog'langan bo'lsa. Kuchsiz havola avtomatik ravishda nil ga aylanadi ob'ekt deinitializatsiya qilinayotganda.
  3. Xarid qilinmagan havolalar (Unowned References): Shuningdek, havolalar sonini oshirmaydi. Agar siz ishonchingiz komil bo'lsa, ob'ekt doimo mavjud bo'ladi, boshqa ob'ekt mavjud bo'lsa, foydalaniladi. Ular opsional bo'lishi mumkin emas (nil). Deinitializatsiya qilingan ob'ektga kirishga urinish xatolik keltirib chiqaradi.

Kuchli havolalar aylanishining misoli:

class Person {
    let name: String
    var apartment: Apartment?

    init(name: String) {
        self.name = name
        print("\(name) boshlanmoqda")
    }

    deinit {
        print("\(name) tugatildi")
    }
}

class Apartment {
    let unit: String
    var tenant: Person?

    init(unit: String) {
        self.unit = unit
        print("Kvartira \(unit) boshlanmoqda")
    }

    deinit {
        print("Kvartira \(unit) tugatildi")
    }
}

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

john!.apartment = unit4A
unit4A!.tenant = john

john = nil // Person deinitializatsiya qilinmaydi
unit4A = nil // Apartment deinitializatsiya qilinmaydi

Muammoni hal qilish uchun, kuchsiz yoki xarid qilinmagan havolalarni ishlating. Bu holatda, tenant Apartmentda kuchsiz havola bo'lishi kerak:

class Person {
    let name: String
    var apartment: Apartment?

    init(name: String) {
        self.name = name
        print("\(name) boshlanmoqda")
    }

    deinit {
        print("\(name) tugatildi")
    }
}

class Apartment {
    let unit: String
    weak var tenant: Person? // Weak reference

    init(unit: String) {
        self.unit = unit
        print("Kvartira \(unit) boshlanmoqda")
    }

    deinit {
        print("Kvartira \(unit) tugatildi")
    }
}

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

john?.apartment = unit4A
unit4A?.tenant = john

john = nil // Person deinitializatsiya qilindi
unit4A = nil // Apartment deinitializatsiya qilindi

Havolalar orasidagi munosabatga qarab, kuchsiz yoki xarid qilinmagan havolani tanlang:

  • Weak: Agar ob'ektlardan biri nil bo'lishi mumkin bo'lsa, boshqa deinitializatsiya qilinishidan oldin (opsional qiymat).
  • Unowned: Agar ob'ekt doimo mavjud bo'lsa, boshqa ob'ekt mavjud bo'lsa, (opsional bo'lmagan qiymat).

ARC, shuningdek, sinflarni qamrab oladigan (closures) xotira bilan ishlashni boshqaradi. Kuchli havolalar aylanishi, agar closure sinf nusxasini qamrab olsa va bu nusxa ham closurega kuchli havola bilan bog'langan bo'lsa, yuzaga kelishi mumkin. Bu muammoni oldini olish uchun, capture listlardan foydalaniladi.

class HTMLElement {
    let name: String
    let text: String?

    lazy var asHTML: () -> String = {
        // Capture list yo'q, kuchli aylanish mumkin
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name)>"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit {
        print("\(name) tugatildi")
    }
}

var paragraph: HTMLElement? = HTMLElement(name: "p", text: "salom, dunyo")
print(paragraph!.asHTML())

paragraph = nil // HTMLElement deinitializatsiya qilinmaydi

Capture list yordamida yechim [weak self]:

class AnHTMLElement {
    let name: String
    let text: String?

    lazy var asHTML: () -> String = { [weak self] in // weak self dan foydalanish
        guard let self = self else { return "" } // self mavjudligini tekshirish
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name)>"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit {
        print("\(name) tugatildi")
    }
}

var paragraph: AnHTMLElement? = AnHTMLElement(name: "p", text: "salom, dunyo")
print(paragraph!.asHTML())

paragraph = nil // AnHTMLElement deinitializatsiya qilindi

[unowned self] foydalanish mumkin, agar siz ishonchingiz komil bo'lsa, sinf nusxasi (self) nil bo'lmaydi, deb.

ARC, qo'lda xotira boshqaruviga nisbatan, Swiftda xotira boshqarishni sezilarli darajada soddalashtiradi, lekin u havolalar turlarini tushunishni talab qiladi, bu esa xotira oqib ketishining oldini oladi.