Что такое KVO/KVM? Для чего их исользуют?ios-6

Что такое KVO?

Key-Value Observing — это механизм в Cocoa/Cocoa Touch, позволяющий объектам наблюдать за изменениями свойств других объектов. Это реализация паттерна Observer в экосистеме Apple.

Основные характеристики KVO:

  • Позволяет подписаться на изменения конкретного свойства
  • Автоматически уведомляет наблюдателей об изменениях
  • Работает только с классами, наследующими от NSObject
  • Требует соблюдения определенных соглашений

Что такое KVM?

Key-Value Monitoring — это более общее понятие, включающее KVO и другие механизмы наблюдения за изменениями значений. В iOS разработке термин KVM используется реже, обычно говорят именно о KVO.

Как работает KVO?

Базовый пример:

class User: NSObject {
    @objc dynamic var name: String = "John Doe"  // 1. Делаем свойство observable
}

let user = User()

// 2. Создаем наблюдатель
var observation: NSKeyValueObservation?

observation = user.observe(\.name, options: [.old, .new]) {
    (object, change) in
    print("Name changed from \(change.oldValue) to \(change.newValue)")
}

user.name = "Jane Smith"  // 3. Триггерим изменение

Основные компоненты KVO

  1. Наблюдаемое свойство:

    • Должно быть помечено как @objc dynamic
    • Должно принадлежать классу, наследующему от NSObject
  2. Наблюдатель (Observer):

    • Реализует callback при изменении значения
    • Хранится как NSKeyValueObservation
  3. Опции наблюдения:

    • .new — получать новое значение
    • .old — получать старое значение
    • .initial — получить начальное значение сразу

Для чего используют KVO?

  1. Связывание данных:

    • Между моделью и UI
    • Между разными компонентами приложения
  2. Наблюдение за системными свойствами:

    // Наблюдение за состоянием UIScrollView
    scrollView.observe(\.contentOffset) { scrollView, _ in
        print("Content offset: \(scrollView.contentOffset)")
    }
    
  3. Интеграция с legacy-кодом:

    • Многие старые API Apple используют KVO
  4. Сложные зависимости между свойствами:

    class Account: NSObject {
        @objc dynamic var balance: Double = 0
        @objc dynamic var isOverdrawn: Bool = false
    
        private var observations = [NSKeyValueObservation]()
    
        init() {
            super.init()
            observations.append(observe(\.balance) { [weak self] _, _ in
                self?.isOverdrawn = (self?.balance ?? 0) < 0
            }
        }
    }
    

Преимущества KVO

  1. Автоматические уведомления: Не нужно вручную вызывать методы при изменении

  2. Низкая связанность: Наблюдателю не нужно знать о наблюдаемом объекте

  3. Множественные наблюдатели: Можно подписать несколько объектов на одно свойство

Недостатки и нюансы

  1. Требования к коду:

    • Наследование от NSObject
    • Помечать свойства как @objc dynamic
  2. Проблемы с памятью:

    • Важно хранить observation, иначе подписка пропадет
    • Использовать [weak self] в замыканиях
  3. Дедлоки: KVO notifications могут приходить синхронно

  4. Отладка:

    • Сложнее отслеживать, чем делегаты или замыкания
    • Могут быть проблемы с thread safety

Альтернативы KVO в Swift

  1. Property Observers:

    var score: Int = 0 {
        didSet {
            print("Score changed from \(oldValue) to \(score)")
        }
    }
    
  2. Combine Framework:

    @Published var username: String = ""
    $username.sink { print("Username changed to \($0)") }
    
  3. RxSwift/ReactiveSwift: Реактивные расширения для наблюдения за изменениями

  4. Делегаты и замыкания: Более явные, но более многословные подходы

Best Practices

  1. Всегда храните наблюдения:

    private var observations = [NSKeyValueObservation]()
    
    func setupObservations() {
        observations.append(object.observe(\.property) { _, _ in
            // handle change
        })
    }
    
  2. Используйте .initial для начальной настройки:

    observe(\.value, options: [.new, .initial]) { _, change in
        // Вызовется сразу с текущим значением
    }
    
  3. Будьте осторожны с потоками: KVO notifications могут приходить с любого потока

  4. Отписывайтесь при необходимости: Установите observations = [] когда они больше не нужны

Резюмируем

KVO — это мощный механизм наблюдения за изменениями свойств, унаследованный от Objective-C. В современной Swift-разработке он используется реже из-за появления более безопасных альтернатив (Combine, property observers), но остается важным для:

  • Работы с legacy-кодом
  • Наблюдения за системными компонентами
  • Случаев, когда нужна низкая связанность компонентов

При использовании KVO важно помнить о:

  1. Требованиях к наблюдаемым свойствам
  2. Правильном управлении памятью
  3. Потокобезопасности
  4. Альтернативах для новых проектов