Sobes.tech
Назад к вопросам
Middle
98
questionbank

Всегда ли нужно использовать weak или unowned в Capture List?

Sobes Copilot

Получайте ответы в реальном времени

Ответ от нейросети

sobes.tech AI

Нет, не всегда.

Используем weak или unowned в Capture List для предотвращения retain cycles.

Retain cycle возникает, когда два или более объекта сильно ссылаются друг на друга, не позволяя памяти быть освобожденной.

Использовать weak или unowned нужно, когда замыкание сильной ссылается на self (или другую переменную экземпляра класса/актора), и в свою очередь self (или другой объект) имеет сильную ссылку на это замыкание.

Примеры ситуаций, где это необходимо:

  • Асинхронные операции (сетевые запросы, обработка данных)
  • Observer паттерн
  • Animation блоки
  • Dispatch queues

Если замыкание не захватывает self (или другую переменную класса/актора, которая имеет сильную ссылку на замыкание), или захватывает структурные типы/перечисления, использование weak или unowned не требуется.

Разница между weak и unowned:

Тип Описание Когда использовать
weak Слабая (опциональная) ссылка. Может стать nil. Когда жизненный цикл замыкания короче или равен жизненному циклу захватываемого объекта, и объект может стать nil до завершения замыкания.
unowned Неосновная (неопциональная) ссылка. Не может стать nil. Когда жизненный цикл замыкания короче или равен жизненному циклу захватываемого объекта, и гарантировано, что объект будет существовать до завершения замыкания. Если объект освободится до выполнения замыкания, произойдет краш (unowned reference to deallocated instance).

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

class MyClass {
    var name: String = "Test"

    func performAsyncOperation() {
        // Замыкание захватывает self.
        // MyClass может быть освобожден до завершения асинхронной операции.
        DispatchQueue.global().async { [weak self] in
            guard let self = self else {
                // self был освобожден, выходим
                return
            }
            print("Operation completed for \(self.name)")
        }
    }

    deinit {
        print("MyClass deallocated")
    }
}

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

class ViewController {
    var dismissButton: UIButton!

    func setupButton() {
        dismissButton.addTarget(self, action: #selector(dismiss), for: .touchUpInside)

        // Замыкание захватывает self.
        // Предполагаем, что `self` (ViewController) живет дольше или столько же, сколько и действие кнопки.
        // В данном случае, таргет-экшн не является замыканием, но иллюстрирует концепцию unowned при strong reference cycle.
        // Если бы это *было* замыкание, например, в обработчике UIControl.Event,
        // и ViewController гарантированно существует во время нажатия кнопки:
        // dismissButton.addAction(UIAction { [unowned self] _ in
        //     self.dismiss(animated: true, completion: nil)
        // }, for: .touchUpInside)
    }

    @objc func dismiss() {
        // Dismiss view controller
    }
}

Если замыкание не образует retain cycle, использование 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 или других объектов, которые могут вызвать retain cycle.
        completion(data)
    }.resume()
}

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