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
weaksind 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