Как замыкания (closures) в Swift захватывают переменные: по значению или по ссылке?

«Как замыкания (closures) в Swift захватывают переменные: по значению или по ссылке?» — вопрос из категории Swift Core, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

По умолчанию — по сильной ссылке (strong reference). Механизм зависит от типа переменной и использования списка захвата (capture list).

  1. Reference-типы (классы): Захватываются по сильной ссылке. Это может создать цикл сильных ссылок (retain cycle).

    class MyClass {
        var value = 0
        lazy var closure: () -> Void = {
            print(self.value) // Захват `self` по сильной ссылке!
        }
    }
  2. Value-типы (структуры, перечисления): По умолчанию также захватываются по ссылке на исходный экземпляр. Чтобы захватить копию значения на момент создания замыкания, используйте список захвата:

    var number = 42
    let closure = { [number] in // Захват КОПИИ значения `number`
        print(number)
    }
    number = 100
    closure() // Выведет: 42
  3. Управление ссылками для классов: Во избежание циклов используйте в списке захвата weak или unowned.

    let closure = { [weak self] in
        guard let self = self else { return }
        self.doSomething() // Безопасная работа с опциональной ссылкой
    }