Sobes.tech
Middle

Mi azok a megtartási ciklusok (retain cycles) az iOS-ben?

sobes.tech MI

Válasz az MI-től

Visszatartási ciklusok akkor keletkeznek, amikor két vagy több objektum erős hivatkozásokkal rendelkezik egymásra, így egy "zárt kör" alakul ki. Ennek eredményeként minden objektum hivatkozásszámlálója soha nem ér el nullát, és nem szabadulhat fel az ARC (Automatikus Hivatkozás Számlálás) által. Ez memória szivárgáshoz vezet, mivel az objektumok akkor is a memóriában maradnak, ha már nem használják őket.

Példa:

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

    init(name: String) {
        self.name = name
        print("\(name) inicializálódik")
    }

    deinit {
        print("\(name) felszabadul")
    }
}

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

    init(unit: String) {
        self.unit = unit
        print("Apartment \(unit) inicializálódik")
    }

    deinit {
        print("Apartment \(unit) felszabadul")
    }
}

Ebben a példában, ha a Person erős hivatkozással rendelkezik az Apartment-re, és az Apartment erős hivatkozással rendelkezik a Person-re, akkor az objektumok felszabadításakor nem kerülnek törlésre, mivel hivatkozásszámlálóik nem érnek el nullát.

Hogyan lehet megoldani a visszatartási ciklusokat:

Használjon gyenge (weak) vagy szehetetlen (unowned) hivatkozásokat.

  • weak: Akkor használjuk, amikor az összekapcsolt objektum nil lehet a másik objektum életciklusán belül. A hivatkozás automatikusan nil lesz, amikor az objektum, amelyre hivatkozik, felszabadul. Ez opcionális.
  • unowned: Akkor használjuk, amikor tudjuk, hogy az összekapcsolt objektumnak ugyanakkor vagy hosszabb életciklusa van, mint a hivatkozó objektumnak. Nem lehet nil.

Javított példa weak használatával:

Az Apartment osztályban a tenant hivatkozást gyenge hivatkozásra változtatjuk:

class Apartment {
    let unit: String
    weak var tenant: Person? // weak használata

    init(unit: String) {
        self.unit = unit
        print("Apartment \(unit) inicializálódik")
    }

    deinit {
        print("Apartment \(unit) felszabadul")
    }
}

Most, amikor a Person objektum felszabadul, a tenant hivatkozás az Apartment-ben automatikusan nil lesz, megszakítva a visszatartási ciklust, és lehetővé téve az Apartment felszabadulását is.

A weak és unowned összehasonlítása:

Jellemző weak unowned
Hivatkozás típusa Opciókénti Nem opcionális
nil lehet? Igen, amikor az objektum felszabadul Nem
Mikor használjuk? Ha az objektum nil lehet Ha az objektum nem lehet nil
Teljesítmény Kicsit lassabb a nil ellenőrzése miatt Gyorsabb

Ezenkívül, a visszatartási ciklusok előfordulhatnak záradékokban is.

Amikor egy záradék rögzíti az instance tulajdonságait vagy a self-t, akkor erős hivatkozást hozhat létre az objektumra. Ha ez az objektum is erős hivatkozással rendelkezik a záradékra (pl. tulajdonság-záradék vagy delegált révén), ciklus alakul ki.

Hogyan lehet megoldani a visszatartási ciklusokat záradékokban:

Használjon fogási listát (capture list).

class SomeClass {
    var value = 0

    func doSomethingDelayed() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in // gyenge fogási lista
            guard let self = self else { return } // `nil` ellenőrzése a gyenge `self`-re
            self.value += 1
            print("Az érték most \(self.value)")
        }
    }
}

Ebben a példában, a [weak self] gyenge hivatkozást hoz létre a self-re a záradékban, elkerülve a ciklus kialakulását.