Sobes.tech
Middle

Как да предотвратим възникването на цикъл на задържане (retain cycle) в iOS?

sobes.tech AI

Отговор от AI

Циклите на задържане се предотвратяват чрез правилната употреба на слаби (weak) или безусловни (unowned) референции.

Слаби референции (weak):

  • Не увеличават брояча на референциите на обекта.
  • Могат да станат nil (nullable).
  • Използват се, когато един обект може да съществува независимо от друг (например, делегат).
class DelegateExample {
    weak var delegate: SomeDelegateType? // Слаба референция към делегата
}

Безусловни unowned референции (unowned):

  • Не увеличават брояча на референциите на обекта.
  • Са гарантирано ненулеви (non-nil).
  • Използват се, когато един обект явно притежава друг, но зависимият обект може да съществува само заедно с притежателя (например, затваряния, които захващат self в клас, където затварянето "принадлежи" явно на инстанцията на класа). Достъпът до unowned референция, когато обектът вече е освободен, ще доведе до грешка по време на изпълнение.
class Owner {
    var closure: (() -> Void)?

    func setupClosure() {
        closure = { [unowned self] in // Безусловна `unowned` референция към self
            // Използване на self
            print("Използване на unowned self")
        }
    }
}

Използване в затваряния: Затварянията могат да създадат цикли на задържане, ако захващат инстанции на класове чрез силни референции, и тази инстанция съдържа също силна референция към затварянето. За да се избегне това, се използват списъци за захващане (capture lists) в началото на затварянето:

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

    lazy var asHTML: () -> String = { [weak self] in // Слаба референция към self
        guard let strongSelf = self else {
            return "" // Обработка на случая, когато self вече е nil
        }
        if let text = strongSelf.text {
            return "<\(strongSelf.name)>\(text)</\(strongSelf.name)>"
        } else {
            return "<\(strongSelf.name)/>"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit {
        print("\(name) се освобождава")
    }
}

Изборът между weak и unowned зависи от семантиката на отношенията между обектите:

  • weak: когато референцията може да стане nil по време на жизнения цикъл на обекта, към който се отнася.
  • unowned: когато обектът, към който се отнася, съществува гарантирано по-дълго или толкова дълго, колкото и обекта, който го отнася, и референцията никога няма да стане nil.