В чем разница между передачей данных через замыкание (Closure) и делегат (Delegate) в Swift?

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

Ответ

Замыкание (Closure)

  • Что это: Анонимная функция, которая может захватывать и хранить ссылки на переменные из окружающего контекста.
  • Идеально для: Одноразовых или простых колбэков (завершение сетевого запроса, обработка результата операции, анимации).
  • Пример:
    
    func loadData(from url: URL, completion: @escaping (Result<Data, Error>) -> Void) {
    URLSession.shared.dataTask(with: url) { data, _, error in
        if let data = data {
            completion(.success(data))
        } else if let error = error {
            completion(.failure(error))
        }
    }.resume()
    }

// Использование loadData(from: someURL) { result in switch result { case .success(let data): print("Данные получены") case .failure(let error): print("Ошибка: (error)") } }

*   **Память:** Риск цикла сильных ссылок. Требует использования `[weak self]` или `[unowned self]` при захвате `self`.

### Делегат (Delegate)
*   **Что это:** Паттерн, где один объект (делегат) поручает другому объекту выполнение определенных задач. Формализуется через **протокол**.
*   **Идеально для:** Долгосрочных взаимодействий с множеством событий или сложной логикой (обработка действий в `UITableViewDelegate`, `UITextFieldDelegate`).
*   **Пример:**
```swift
protocol DataManagerDelegate: AnyObject {
    func didReceiveData(_ data: Data)
    func didFailWithError(_ error: Error)
}

class DataManager {
    weak var delegate: DataManagerDelegate? // Обязательно weak!

    func fetchData() {
        // ... логика загрузки
        if success {
            delegate?.didReceiveData(someData)
        } else {
            delegate?.didFailWithError(someError)
        }
    }
}

// Класс, подписанный на делегата
class ViewController: UIViewController, DataManagerDelegate {
    let manager = DataManager()

    override func viewDidLoad() {
        super.viewDidLoad()
        manager.delegate = self
        manager.fetchData()
    }

    func didReceiveData(_ data: Data) { /* Обновить UI */ }
    func didFailWithError(_ error: Error) { /* Показать ошибку */ }
}
  • Память: Использование протокола с AnyObject и weak ссылки предотвращает циклы удержания.

Сравнение

Критерий Замыкание (Closure) Делегат (Delegate)
Назначение Одноразовые/простые колбэки Множественные события, сложные взаимодействия
Связность Высокая (захватывает контекст) Низкая (связь через протокол)
Память Риск цикла, нужен [weak] Безопаснее (требует weak ссылки)
Читаемость Может ухудшиться при вложенности Четкая структура через методы протокола
Гибкость Легко создать на месте Требует объявления протокола и его реализации