В чем разница между паттерном делегирования (delegation) и Key-Value Observing (KVO) в iOS?

«В чем разница между паттерном делегирования (delegation) и Key-Value Observing (KVO) в iOS?» — вопрос из категории Паттерны, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Делегирование — это строго типизированный паттерн проектирования, при котором один объект (делегат) поручает другому объекту выполнение конкретных задач или реагирование на события. Реализуется через протоколы.

Key-Value Observing (KVO) — это механизм среды выполнения (runtime), позволяющий объектам наблюдать за изменениями значений свойств других объектов. Реализуется через наследование от NSObject и аннотации @objc dynamic.

Сравнение: Критерий Делегирование Key-Value Observing (KVO)
Типизация Строгая (протоколы) Слабая (строковые ключи)
Связь Один-на-один (один делегат) Один-ко-многим (много наблюдателей)
Время связи Компиляция Выполнение (runtime)
Зависимость от Objective-C Нет (чистый Swift) Да (NSObject, @objc dynamic)
Сложность Простота и ясность Риск крашей (неправильный ключ, забытый removeObserver)
Типичное использование Обработка событий таблицы (UITableViewDelegate), кастомные callback Наблюдение за свойствами системных классов (например, AVPlayer), реактивное связывание в legacy-коде

Пример делегирования:

protocol DataLoaderDelegate: AnyObject {
    func dataLoader(_ loader: DataLoader, didLoad data: Data)
    func dataLoader(_ loader: DataLoader, didFailWith error: Error)
}

class DataLoader {
    weak var delegate: DataLoaderDelegate?
    func load() {
        // ... Загрузка
        delegate?.dataLoader(self, didLoad: Data())
    }
}

Пример KVO:

class PlayerObserver: NSObject {
    @objc var player: AVPlayer
    private var observation: NSKeyValueObservation?

    init(player: AVPlayer) {
        self.player = player
        super.init()
        // Наблюдаем за изменением статуса
        observation = observe(.player.status, options: [.new]) { _, change in
            print("Status changed to: (change.newValue!)")
        }
    }
}
// В Swift 4+ рекомендуется использовать блоки `observe(...)` вместо `addObserver`.

Вывод: Используйте делегирование для четко определенных событий и взаимодействия между вашими объектами. KVO применяйте в основном для наблюдения за свойствами существующих классов Cocoa, которые его поддерживают, с осторожностью из-за рисков runtime.