Sobes.tech
Middle

Механизми идоракунии ёддоштӣ (ARC) дар забони Swift чӣ гуна кор мекунад?

sobes.tech AI

Ҷавоб аз AI

ARC (Автоматичне обчислення посилань) автоматично відстежує та керує посиланнями на екземпляри класів. Він звільняє пам’ять, зайняту екземпляром, коли на нього більше немає сильних посилань. ARC працює лише з екземплярами класів, а не з структурами або перерахуваннями, оскільки вони є типами-значеннями.

Три типи зв’язків між екземплярами:

  • Strong (Сильна): Зв’язок, який збільшує лічильник посилань на екземпляр. За замовчуванням всі посилання є сильними.
  • Weak (Слабка): Зв’язок, який не збільшує лічильник посилань. Використовується для запобігання циклам сильних посилань. Посилання типу weak завжди є опціональними, оскільки екземпляр може бути звільнений у будь-який момент.
  • Unowned (Безхазяйна): Зв’язок, який не збільшує лічильник посилань і використовується, коли гарантовано, що пов’язаний екземпляр буде існувати довше за поточний. Не обов’язково, але при спробі доступу до звільненого екземпляра виникає помилка виконання.

Цикли сильних посилань виникають, коли два або більше екземплярів класу мають сильні посилання один на одного, запобігаючи їх звільненню ARC. Для вирішення циклів використовуються weak або unowned посилання.

Приклад циклу сильних посилань:

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

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

    deinit { // викликається при звільненні
        print("\(name) звільняється")
    }
}

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

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

    deinit { // викликається при звільненні
        print("Квартира \(unit) звільняється")
    }
}

var john: Person?
var unit4A: Apartment?

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

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

john = nil
unit4A = nil // Нічого не виводиться, оскільки виник цикл сильних посилань

Рішення циклу сильних посилань за допомогою weak:

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

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

    deinit {
        print("\(name) звільняється")
    }
}

class Apartment {
    let unit: String
    weak var tenant: Person? // Слабке посилання на Person

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

    deinit {
        print("Квартира \(unit) звільняється")
    }
}

var john: Person?
var unit4A: Apartment?

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

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

john = nil // Звільняє Person
unit4A = nil // Звільняє Apartment

Рішення циклу сильних посилань за допомогою unowned:

class Customer {
    let name: String
    var card: CreditCard?

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

    deinit {
        print("\(name) звільняється")
    }
}

class CreditCard {
    let number: UInt64
    unowned let customer: Customer // Безхазяйне посилання на Customer

    init(number: UInt64, customer: Customer) {
        self.number = number
        self.customer = customer
    }

    deinit {
        print("Карта #\(number) звільняється")
    }
}

var john: Customer?

john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)

john = nil // Звільняє Customer і CreditCard

ARC у замиканнях: Замикання також можуть створювати цикли посилань, захоплюючи екземпляри класів. Для запобігання використовуються списки захоплення ([weak self] або [unowned self]).

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

    lazy var asHTML: () -> String = { // lazy для можливості використовувати self до ініціалізації
        [unowned self] in // список захоплення з "unowned self"
        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) звільняється")
    }
}

var paragraph: HTMLElement? = HTMLElement(name: "p", text: "привіт, світ")
print(paragraph!.asHTML())

paragraph = nil // Звільняє HTMLElement