Sobes.tech
Middle

Винаги ли е необходимо да използвате weak или unowned в списъка за улавяне?

sobes.tech AI

Отговор от AI

Не, не винаги.

Използваме weak или unowned в Capture List за предотвратяване на цикли на задържане.

Цикълът на задържане възниква, когато две или повече обекта се позовават силно един на друг, пречейки освобождаването на паметта.

Използването на weak или unowned е необходимо, когато затварянето силно се позовава на self (или друга променлива на инстанция на класа/актора), и от своя страна self (или друг обект) има силна препратка към това затваряне.

Примери за ситуации, когато е необходимо:

  • Асинхронни операции (мрежови заявки, обработка на данни)
  • Модел Observer
  • Анимационни блокове
  • Dispatch опашки

Ако затварянето не улавя self (или друга променлива на класа/актора, която има силна препратка към затварянето), или улавя структурни типове/енумерации, използването на weak или unowned не е необходимо.

Разликата между weak и unowned:

Тип Описание Кога да използвате
weak Слаба (опционална) препратка. Може да стане nil. Когато жизненият цикъл на затварянето е по-кратък или равен на този на обекта, който се улавя, и обектът може да стане nil преди завършването на затварянето.
unowned Неосновна (неопционална) препратка. Не може да стане nil. Когато жизненият цикъл на затварянето е по-кратък или равен на този на обекта, който се улавя, и гарантирано обектът ще съществува до завършването на затварянето. Ако обектът бъде освободен преди изпълнението на затварянето, ще настъпи срив (unowned референция към деактивиран инстанс).

Пример за използване на weak в Capture List:

class MyClass {
    var name: String = "Test"

    func performAsyncOperation() {
        DispatchQueue.global().async { [weak self] in
            guard let self = self else {
                return
            }
            print("Операцията приключи за \(self.name)")
        }
    }

    deinit {
        print("MyClass е освободен")
    }
}

Пример за използване на unowned в Capture List:

class ViewController {
    var dismissButton: UIButton!

    func setupButton() {
        dismissButton.addTarget(self, action: #selector(dismiss), for: .touchUpInside)
        // Този пример показва, че `self` (ViewController) живее по-дълго или толкова дълго, колкото и действието на бутона.
        // Target-action не е затваряне, но илюстрира концепцията за unowned при силен цикъл на задържане.
        // Ако това беше затваряне, например, в обработчика на UIControl.Event,
        // и ViewController гарантирано съществува по време на натискането на бутона:
        // dismissButton.addAction(UIAction { [unowned self] _ in
        //     self.dismiss(animated: true, completion: nil)
        // }, for: .touchUpInside)
    }

    @objc func dismiss() {
        // Затваряне на изгледовия контролер
    }
}

Ако затварянето не създава цикъл на задържане, използването на weak или unowned не е необходимо и може дори да доведе до ненужни проверки (в случай на weak) или потенциални сривове (в случай на unowned).

Например, затваряне, което не улавя self или друг обект, който има силна препратка към затварянето:

func fetchData(completion: @escaping (Data) -> Void) {
    URLSession.shared.dataTask(with: URL(string: "...")!) { data, response, error in
        guard let data = data else { return }
        // Тук няма улавяне на `self` или други обекти, които биха могли да причинят цикъл на задържане.
        completion(data)
    }.resume()
}

В този случай [weak self] или [unowned self] в Capture List не са необходими.