Для чего используется `[weak self]` в замыканиях Swift и приведи пример?

«Для чего используется `[weak self]` в замыканиях Swift и приведи пример?» — вопрос из категории Управление памятью, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

[weak self] используется в списке захвата замыкания для предотвращения циклических сильных ссылок (retain cycles). Он создает слабую (weak) ссылку на self, которая автоматически становится nil, если объект, на который она указывает, уничтожается.

Пример: асинхронная загрузка данных в UIViewController.

class MyViewController: UIViewController {
    let networkService = NetworkService()

    func fetchUserData() {
        // Захватываем weak-ссылку на self, чтобы избежать retain cycle:
        // ViewController -> networkService -> closure -> (strong) ViewController
        networkService.fetchData { [weak self] result in
            // 1. Проверяем, что self еще существует (не был освобожден)
            guard let self = self else {
                print("ViewController deallocated, ignoring callback.")
                return
            }
            // 2. Используем self для обновления UI
            self.updateUI(with: result)
            self.saveToCache(result)
        }
    }

    private func updateUI(with result: DataResult) { /* ... */ }
    private func saveToCache(_ result: DataResult) { /* ... */ }
}

Когда использовать [weak self]:

  1. Асинхронные операции: При работе с DispatchQueue, URLSession, Operation, коллбэками, где self может быть освобожден до выполнения замыкания.
  2. Замыкания, хранимые в свойствах: Когда замыкание, захватывающее self, сохраняется в свойстве того же самого объекта (например, обработчик в UIButton).

Альтернатива [unowned self]: Используется, когда вы уверены, что объект (self) точно переживет замыкание. При нарушении этого условия приведет к крашу (EXC_BAD_ACCESS). Пример: анимация, которая точно завершится до деаллокации контроллера.

UIView.animate(withDuration: 0.3) { [unowned self] in
    self.view.alpha = 0.0 // Рискованно, если контроллер может быть освобожден во время анимации.
}

Best practice: В большинстве асинхронных сценариев безопаснее использовать [weak self] с последующей проверкой через guard let или optional chaining (self?.updateUI()).