Sobes.tech
Middle

Hoe werkt het mechanisme voor automatische geheugenbeheer (ARC) in de Swift-taal?

sobes.tech AI

Antwoord van AI

ARC (Automatische Referentie Telling) volgt automatisch en beheert de referenties naar klasse-instanties. Het vrijmaakt geheugen dat door een instantie wordt ingenomen wanneer er geen sterke referenties meer naar zijn. ARC werkt alleen met klasse-instanties, niet met structuren of enumeraties, omdat deze waarde-typen zijn.

Drie soorten referenties tussen instanties:

  • Strong (Sterk): Een referentie die de referentieteller van een instantie verhoogt. Standaard zijn alle referenties sterk.
  • Weak (Zwak): Een referentie die de teller niet verhoogt. Gebruikt om cycli van sterke referenties te voorkomen. weak-referenties zijn altijd optioneel, omdat de instantie op elk moment kan worden vrijgemaakt.
  • Unowned (Ongeowned): Een referentie die de teller niet verhoogt en wordt gebruikt wanneer gegarandeerd is dat de gekoppelde instantie langer zal bestaan dan de huidige. Niet verplicht, maar het proberen te benaderen van een vrijgemaakte instantie veroorzaakt een runtime-fout.

Sterke referentiecycli ontstaan wanneer twee of meer klasse-instanties sterke referenties naar elkaar hebben, waardoor hun vrijgave door ARC wordt voorkomen. Om cycli op te lossen, worden weak of unowned referenties gebruikt.

Voorbeeld van een sterke referentiecyclus:

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

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

    deinit { // wordt aangeroepen bij vrijgave
        print("\(name) wordt gedeinitialiseerd")
    }
}

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

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

    deinit { // wordt aangeroepen bij vrijgave
        print("Appartement \(unit) wordt gedeinitialiseerd")
    }
}

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 // Niets wordt afgedrukt, omdat er een cyclus van sterke referenties is

Oplossen van een sterke referentiecyclus met weak:

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

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

    deinit {
        print("\(name) wordt gedeinitialiseerd")
    }
}

class Apartment {
    let unit: String
    weak var tenant: Person? // Zwakke referentie naar Person

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

    deinit {
        print("Appartement \(unit) wordt gedeinitialiseerd")
    }
}

var john: Person?
var unit4A: Apartment?

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

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

john = nil // Verwijdert Person
unit4A = nil // Verwijdert Apartment

Oplossen van een sterke referentiecyclus met unowned:

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

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

    deinit {
        print("\(name) wordt gedeinitialiseerd")
    }
}

class CreditCard {
    let number: UInt64
    unowned let customer: Customer // Ongeowned referentie naar Customer

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

    deinit {
        print("Kaart #\(number) wordt gedeinitialiseerd")
    }
}

var john: Customer?

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

john = nil // Verwijdert Customer en CreditCard

ARC in closures: Closures kunnen ook sterke cycli van referenties creëren door instanties van klassen te vangen. Om dit te voorkomen, worden capture-lijsten gebruikt ([weak self] of [unowned self]).

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

    lazy var asHTML: () -> String = { // lazy om self te kunnen gebruiken vóór initialisatie
        [unowned self] in // Capture-lijst met "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) wordt gedeinitialiseerd")
    }
}

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

paragraph = nil // Verwijdert HTMLElement