Назад к вопросам
Middle+
95
questionbank

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

Sobes Copilot

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

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

sobes.tech AI

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

Weak и unowned используются для предотвращения циклов сильных ссылок (retain cycles) в замыканиях, когда замыкание удерживает сильную ссылку на объект, а объект в свою очередь удерживает сильную ссылку на замыкание.

Использовать weak или unowned необходимо, когда замыкание захватывает сильную ссылку на self или другой ссылочный тип, и существует вероятность возникновения цикла сильных ссылок.

Ситуации, когда не нужно использовать weak/unowned:

  • Замыкание захватывает типы значений (структуры, перечисления, кортежи). Они копируются при захвате, и циклы сильных ссылок не возникают.
  • Замыкание захватывает ссылочный тип, но не существует двусторонней сильной ссылки. Например, если замыкание создается локально внутри метода и не сохраняется объектом, на который ссылается.
  • Замыкание является коротким по времени жизни и выполнится до того, как объект, на который оно ссылается, будет освобожден. Например, замыкание, переданное в метод как Completion Handler, который немедленно выполняется.

Выбор между weak и unowned:

  • weak: Используется, когда захваченный объект может стать nil в любое время до выполнения замыкания. Ссылка объявлена как опциональная (ObjectType?). Требует проверки на nil перед использованием.
  • unowned: Используется, когда известно, что захваченный объект всегда будет существовать на момент выполнения замыкания. Ссылка неопциональная (ObjectType). Не требует проверки на nil. Использование unowned для объекта, который стал nil, приведет к крашу (runtime error).
// Пример, когда не нужен weak/unowned (захват структуры)
struct MyStruct {
    var value: Int
}

let myStruct = MyStruct(value: 10)
let closure = {
    print(myStruct.value) // Захватывает копию myStruct
}
closure()

// Пример, когда не нужен weak/unowned (локальное замыкание)
class MyClass {
    func doSomething() {
        let localValue = 5
        let localClosure = {
            print(self.description) // Захватывает self, но замыкание не сохраняется MyClass
        }
        localClosure()
    }

    var description: String {
        return "Instance of MyClass"
    }
}

let myObject = MyClass()
myObject.doSomething()

// Пример, когда нужен weak (потенциально nil)
class AsyncOperation {
    var completionHandler: (() -> Void)?

    func performOperation() {
        // Имитация асинхронной работы
        DispatchQueue.global().async {
            print("Operation completed")
            self.completionHandler?() // nil-optional chaining
        }
    }

    deinit {
        print("AsyncOperation deinitialized")
    }
}

class ViewController {
    var op: AsyncOperation?

    func setupOperation() {
        op = AsyncOperation()
        op?.completionHandler = { [weak self] in // weak self
            guard let self = self else { return }
            print("Completion handler in ViewController: \(self)")
        }
    }

    deinit {
        print("ViewController deinitialized")
    }
}

// Пример, когда нужен unowned (гарантированно существует)
class Button {
    var action: (() -> Void)?

    func tap() {
        action?()
    }

    deinit {
        print("Button deinitialized")
    }
}

class ViewControllerWithButton {
    var button: Button

    init() {
        button = Button()
        button.action = { [unowned self] in // unowned self
            print("Button tapped in ViewControllerWithButton: \(self)")
        }
    }

    deinit {
        print("ViewControllerWithButton deinitialized")
    }
}