Sobes.tech
Middle

Jak funguje mechanismus automatického řízení paměti (ARC) v jazyce Swift?

sobes.tech AI

Odpověď od AI

ARC (Automatické počítání odkazů) automaticky sleduje a spravuje odkazy na instance tříd. Uvolňuje paměť obsazenou instancí, když na ni již nejsou žádné silné odkazy. ARC funguje pouze s instancemi tříd, nikoli se strukturami nebo výčty, protože ty jsou hodnotové typy.

Tři typy odkazů mezi instancemi:

  • Strong (Silný): Odkaz, který zvyšuje čítač odkazů na instanci. Výchozí jsou všechny odkazy silné.
  • Weak (Slabý): Odkaz, který nezvyšuje čítač odkazů. Používá se k prevenci cyklů silných odkazů. Odkazy weak jsou vždy volitelné, protože instance může být uvolněna kdykoli.
  • Unowned (Nezávislý): Odkaz, který nezvyšuje čítač odkazů a používá se, když je zaručeno, že přidružená instance bude existovat déle než aktuální. Není povinný, ale pokus o přístup k uvolněné instanci způsobí běhovou chybu.

Cyklus silných odkazů vzniká, když dvě nebo více instancí třídy má navzájem silné odkazy, což brání jejich uvolnění ARC. Pro řešení těchto cyklů se používají odkazy weak nebo unowned.

Příklad cyklu silných odkazů:

class Person {
    let jméno: String
    var byt: Apartment?

    init(jméno: String) {
        self.jméno = jméno
    }

    deinit { // volá se při uvolnění
        print("\(jméno) je uvolněn")
    }
}

class Apartment {
    let jednotka: String
    var nájemník: Person?

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

    deinit { // volá se při uvolnění
        print("Byt \(jednotka) je uvolněn")
    }
}

var john: Person?
var jednotka4A: Apartment?

john = Person(jméno: "John Appleseed")
jednotka4A = Apartment(jednotka: "4A")

john!.byt = jednotka4A
jednotka4A!.nájemník = john

john = nil
jednotka4A = nil // Nic se nezobrazí, protože je cyklus silných odkazů

Řešení cyklu silných odkazů pomocí weak:

class Person {
    let jméno: String
    var byt: Apartment?

    init(jméno: String) {
        self.jméno = jméno
    }

    deinit {
        print("\(jméno) je uvolněn")
    }
}

class Apartment {
    let jednotka: String
    weak var nájemník: Person? // Slabý odkaz na Person

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

    deinit {
        print("Byt \(jednotka) je uvolněn")
    }
}

var john: Person?
var jednotka4A: Apartment?

john = Person(jméno: "John Appleseed")
jednotka4A = Apartment(jednotka: "4A")

john!.byt = jednotka4A
jednotka4A!.nájemník = john

john = nil // Uvolní Person
jednotka4A = nil // Uvolní Apartment

Řešení cyklu silných odkazů pomocí unowned:

class Customer {
    let jméno: String
    var karta: CreditCard?

    init(jméno: String) {
        self.jméno = jméno
    }

    deinit {
        print("\(jméno) je uvolněn")
    }
}

class CreditCard {
    let číslo: UInt64
    unowned let zákazník: Customer // Nevlastní odkaz na Customer

    init(číslo: UInt64, zákazník: Customer) {
        self.číslo = číslo
        self.zákazník = zákazník
    }

    deinit {
        print("Karta #\(číslo) je uvolněna")
    }
}

var john: Customer?

john = Customer(jméno: "John Appleseed")
john!.karta = CreditCard(číslo: 1234_5678_9012_3456, zákazník: john!)

john = nil // Uvolní Customer a CreditCard

ARC v uzávěrech: Uzávěry mohou také vytvářet cykly silných odkazů tím, že zachycují instance tříd. K tomu se používají seznamy zachycení ([weak self] nebo [unowned self]).

class HTMLElement {
    let jméno: String
    let text: String?

    lazy var jakoHTML: () -> String = { // lazy pro možnost použití self před inicializací
        [unowned self] in // Seznam zachycení s "unowned self"
        if let text = self.text {
            return "<\(self.jméno)>\(text)</\(self.jméno)>"
        } else {
            return "<\(self.jméno) />"
        }
    }

    init(jméno: String, text: String? = nil) {
        self.jméno = jméno
        self.text = text
    }

    deinit {
        print("\(jméno) je uvolněn")
    }
}

var odstavec: HTMLElement? = HTMLElement(jméno: "p", text: "hello, world")
print(odstavec!.jakoHTML())

odstavec = nil // Uvolní HTMLElement