Sobes.tech
Middle

Wie funktioniert der Mechanismus der automatischen Speicherverwaltung (ARC) in der Swift-Sprache?

sobes.tech KI

Antwort von AI

ARC (Automatisches Referenzzählen) verfolgt und verwaltet automatisch Referenzen auf Instanzen von Klassen. Es gibt den Speicher frei, der von einer Instanz belegt wird, wenn keine starken Referenzen mehr auf sie bestehen. ARC funktioniert nur mit Instanzen von Klassen, nicht mit Strukturen oder Aufzählungen, da diese Werttypen sind.

Drei Arten von Verbindungen zwischen Instanzen:

  • Strong (Stark): Eine Verbindung, die den Referenzzähler der Instanz erhöht. Standardmäßig sind alle Referenzen stark.
  • Weak (Schwach): Eine Verbindung, die den Referenzzähler nicht erhöht. Wird verwendet, um starke Referenzzyklen zu vermeiden. Referenzen vom Typ weak sind immer optional, da die Instanz jederzeit freigegeben werden kann.
  • Unowned (Unbesessen): Eine Verbindung, die den Referenzzähler nicht erhöht und verwendet wird, wenn garantiert ist, dass die verbundene Instanz länger existiert als die aktuelle. Nicht zwingend, aber der Zugriff auf eine freigegebene Instanz führt zu einem Laufzeitfehler.

Starke Referenzzyklen entstehen, wenn zwei oder mehr Instanzen einer Klasse starke Referenzen aufeinander haben, was ihre Freigabe durch ARC verhindert. Zur Behebung dieser Zyklen werden weak oder unowned Referenzen verwendet.

Beispiel für einen starken Referenzzyklus:

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

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

    deinit { // Wird beim Freigeben aufgerufen
        print("\(name) wird deinitialisiert")
    }
}

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

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

    deinit { // Wird beim Freigeben aufgerufen
        print("Apartment \(unit) wird deinitialisiert")
    }
}

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 // Es wird nichts ausgegeben, da ein starker Referenzzyklus besteht

Auflösung des Zyklus mit weak:

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

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

    deinit {
        print("\(name) wird deinitialisiert")
    }
}

class Apartment {
    let unit: String
    weak var tenant: Person? // Schwache Referenz auf Person

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

    deinit {
        print("Apartment \(unit) wird deinitialisiert")
    }
}

var john: Person?
var unit4A: Apartment?

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

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

john = nil // Gibt Person frei
unit4A = nil // Gibt Apartment frei

Auflösung mit unowned:

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

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

    deinit {
        print("\(name) wird deinitialisiert")
    }
}

class CreditCard {
    let number: UInt64
    unowned let customer: Customer // Unbesessene Referenz auf Customer

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

    deinit {
        print("Karte #\(number) wird deinitialisiert")
    }
}

var john: Customer?

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

john = nil // Gibt Customer und CreditCard frei

ARC in Closures: Closures können ebenfalls starke Referenzzyklen erzeugen, indem sie Instanzen von Klassen capturen. Um dies zu vermeiden, werden Capture-Listen ([weak self] oder [unowned self]) verwendet.

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

    lazy var asHTML: () -> String = { // lazy, um self vor der Initialisierung verwenden zu können
        [unowned self] in // Capture-Liste mit "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) wird deinitialisiert")
    }
}

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

paragraph = nil // Gibt HTMLElement frei