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 се освобождава