Was sind Retain Cycles in iOS?
sobes.tech KI
Antwort von AI
Retain-Cycles treten auf, wenn zwei oder mehr Objekte starke Referenzen aufeinander haben, wodurch ein "geschlossener Kreis" entsteht. Infolgedessen erreicht der Referenzzähler jedes Objekts niemals null und es kann nicht durch ARC (Automatisches Referenzzählen) freigegeben werden. Dies führt zu Speicherlecks, da die Objekte im Speicher verbleiben, auch nachdem sie nicht mehr verwendet werden.
Beispiel:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) wird initialisiert")
}
deinit {
print("\(name) wird deinitialisiert")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
print("Apartment \(unit) wird initialisiert")
}
deinit {
print("Apartment \(unit) wird deinitialisiert")
}
}
In diesem Beispiel, wenn Person eine starke Referenz auf Apartment hat, und Apartment eine starke Referenz auf Person, werden diese Objekte beim Versuch, sie freizugeben, nicht gelöscht, da ihre Referenzzähler nicht null erreichen.
Wie man das Problem der Retain-Cycles löst:
Verwenden Sie schwache (weak) oder unowned Referenzen.
weak: Wird verwendet, wenn das verknüpfte Objekt möglicherweisenilwird, während das andere Objekt lebt. Die Referenz wird automatischnil, wenn das referenzierte Objekt freigegeben wird. Es ist optional.unowned: Wird verwendet, wenn bekannt ist, dass das verknüpfte Objekt die gleiche oder eine längere Lebensdauer hat als das Objekt mit der Referenz. Es kann nichtnilsein.
Beispiel mit weak:
Ändern wir die Referenz tenant in der Klasse Apartment zu weak:
class Apartment {
let unit: String
weak var tenant: Person? // Verwendung von weak
init(unit: String) {
self.unit = unit
print("Apartment \(unit) wird initialisiert")
}
deinit {
print("Apartment \(unit) wird deinitialisiert")
}
}
Wenn das Person-Objekt freigegeben wird, wird die Referenz tenant in Apartment automatisch nil, wodurch der Retain-Cycle gebrochen wird und Apartment ebenfalls freigegeben werden kann.
Vergleichstabelle weak und unowned:
| Eigenschaft | weak |
unowned |
|---|---|---|
| Referenztyp | Optional | Nicht optional |
Kann nil sein? |
Ja, bei Freigabe des Objekts | Nein |
| Wann verwenden | Wenn das verknüpfte Objekt nil werden kann |
Wenn bekannt ist, dass das Objekt nicht nil wird |
| Leistung | Etwas langsamer wegen nil-Prüfung |
Etwas schneller |
Außerdem können Retain-Cycles auch in Closures auftreten.
Wenn ein Closure Eigenschaften einer Instanz oder self erfasst, kann es eine starke Referenz auf das Objekt erstellen. Wenn dieses Objekt ebenfalls eine starke Referenz auf das Closure hat (z.B. durch eine Closure-Eigenschaft oder einen Delegate), entsteht ein Cycle.
Wie man Retain-Cycles in Closures vermeidet:
Verwenden Sie Capture List.
class SomeClass {
var value = 0
func doSomethingDelayed() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in // Capture list mit weak self
guard let self = self else { return } // `self` auf `nil` prüfen
self.value += 1
print("Wert ist jetzt \(self.value)")
}
}
}
In diesem Beispiel erstellt [weak self] eine schwache Referenz auf self im Closure, um einen Retain-Cycle zu vermeiden.