Wat zijn retain cycles in iOS?
sobes.tech AI
Antwoord van AI
Retentiecycli ontstaan wanneer twee of meer objecten sterke verwijzingen naar elkaar hebben, waardoor een "gesloten kring" ontstaat. Als gevolg hiervan bereikt de referentieteller van elk object nooit nul en kan het niet worden vrijgegeven door ARC (Automatische Referentietelling). Dit leidt tot geheugenlekken, omdat de objecten in het geheugen blijven, zelfs nadat ze niet meer worden gebruikt.
Voorbeeld:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) wordt geïnitialiseerd")
}
deinit {
print("\(name) wordt gedeinitialiseerd")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
print("Appartement \(unit) wordt geïnitialiseerd")
}
deinit {
print("Appartement \(unit) wordt gedeinitialiseerd")
}
}
In dit voorbeeld, als Person een sterke verwijzing heeft naar Apartment, en Apartment heeft een sterke verwijzing naar Person, worden deze objecten niet verwijderd wanneer geprobeerd wordt ze vrij te geven, omdat hun referentietellers niet nul worden.
Hoe los je het probleem van retentiecycli op:
Gebruik zwakke (weak) of onbezette (unowned) verwijzingen.
weak: Wordt gebruikt wanneer het mogelijk is dat het gerelateerde objectnilwordt tijdens de levensduur van een ander object. De verwijzing wordt automatischnilwanneer het object waarnaar wordt verwezen wordt vrijgegeven. Het is optioneel.unowned: Wordt gebruikt wanneer bekend is dat het gerelateerde object dezelfde of een langere levensduur heeft dan het object dat ernaar verwijst. Het kan nietnilzijn.
Voorbeeld met weak:
Laten we de verwijzing tenant in de klasse Apartment zwak maken:
class Apartment {
let unit: String
weak var tenant: Person? // gebruiken van weak
init(unit: String) {
self.unit = unit
print("Appartement \(unit) wordt geïnitialiseerd")
}
deinit {
print("Appartement \(unit) wordt gedeinitialiseerd")
}
}
Nu, wanneer het Person object wordt vrijgegeven, wordt de verwijzing tenant in Apartment automatisch nil, waardoor de cyclus wordt verbroken en Apartment ook kan worden vrijgegeven.
Vergelijkingstabel weak en unowned:
| Kenmerk | weak |
unowned |
|---|---|---|
| Type verwijzing | Optioneel | Niet-optioneel |
Kan nil zijn? |
Ja, wanneer het object wordt vrijgegeven | Nee |
| Wanneer gebruiken? | Wanneer het gerelateerde object nil kan worden |
Wanneer het object niet nil zal worden |
| Prestaties | Iets langzamer vanwege nil-controle |
Iets sneller |
Bovendien kunnen retentiecycli ook optreden in closures.
Wanneer een closure eigenschappen van een instantie of self vastlegt, kan het een sterke verwijzing naar het object creëren. Als dat object ook een sterke verwijzing naar de closure heeft (bijvoorbeeld via een closure-eigenschap of delegate), ontstaat er een cyclus.
Hoe los je retentiecycli in closures op:
Gebruik capture list.
class SomeClass {
var value = 0
func doSomethingDelayed() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in // capture list met weak self
guard let self = self else { return } // `nil` controle voor weak self
self.value += 1
print("Waarde is nu \(self.value)")
}
}
}
In dit voorbeeld creëert [weak self] een zwakke verwijzing naar self binnen de closure, waardoor een retentiecyclus wordt voorkomen.