KVO(Key Value Observing)란 무엇인가?

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


KVO(Key-Value Observing)

특정 키의 값의 변화를 감지하기 위한 기능

모델 객체의 어떤 값이 변경되었을 경우 이를 UI에 반영하기 위해서 컨트롤러는 모델 객체에 Observing을 도입하여 델리게이트에 특정 메시지를 보내 처리할 수 있도록 하는 것

즉, 변수에 코드를 붙여 변수가 변경될 때마다 코드가 실행되도록 하는 방법을 의미한다. property observers(willset , didSet)과 아주 유사한데 KVO는 타입 정의 밖에서 observe를 추가한다는 점이 다르다. KVO는 순수 스위프트 코드로는 그리 좋지 않은데, 그 이유는 Objective-c 런타임에 의존 하고 있기 때문이다. 그래서 NSObject를 상속받기 위해 @objc 를 반드시 붙여줘야 한다. 특히 KVO는 속성 각각에 @objc dynamic 을 붙여줘야 한다.

dynamic 은 objective-c 의 문법의 하나로, 특정 method나 function의 구현을 objective-c 런타임에서 하겠다고 결정하는 것이다.
예를들어, 하위클래스가 상위 클래스의 메서드를 override할 때 dynamic dispatch는 어떤 메서드의 구현해야 하는지 파악한다.

Example

class SomeClass: NSObject {
  @objc dynamic var value: String = ""
}

let someObject = SomeClass()

someObject.observe(\.value) { (object, change) in
  print("SomeClass object value changed to \(object.value)")
}

someObject.value = "test"  // TEST

위 코드는 NSObject를 상속받은 SomeClass클래스를 정의하고 있다. NSObject는 KVO지원을 위해서는 필수인데 KVO 기능이 NSObject에 구현되어 있기 때문이다. 그리고 유일한 프로퍼티인 value앞에는 dynamic이라는 수식어가 붙어있는데 이는 dynamic dispatch를 활성화 시키는 오퍼레이터다. 키패스(KeyPath = /.)이름을 이용해 프로퍼티의 주소를 찾게 해 주도록 하라는 정도라고 할 수 있을 것 같다.

핵심적인 부분은 옵저버를 심기 위한 observe()메소드이다. 이 observe()메소드는 기존 3.x 버전들과는 다르게 클로져를 이용할 수 있게 추가된 메소드다. 즉 해당 오브젝트에서 키패스로 지정된 프로퍼티의 값이 바뀌게 되면 클로져가 호출되게 된다. 귀찮게 오버라이드를 쓸 필요가 없어졌다. 심지어 컨텍스트 같이 몰라도 되는 값은 아예 보이지 않게 되었다. 이 부분은 정말 Swift친화적으로 바뀌었다. 예제의 observe()명령으로 someObject의 value라는 프로퍼티의 변화를 감지하기 시작한다. 결과적으로 마지막 줄에서 someObject.value 값을 바꾸자마자 “SomeClass object value changed to test” 라는 로그가 콘솔에 찍히게 된다.

좀더 이해하기 쉬운 예제로는 아래와 같다.

@objc class Person: NSObject {
    @objc dynamic var name = "Zehye"
}

let zehye = Person

그리고 이제 처음에 말했듯이 상태 변화를 감지하기 위한 코드를 붙여, 변수가 변경될 때마다 코드가 실행되도록 해보자.

zehye.observe(\Person.name, options: .new) { person, change in
    print("I'm now called \(person.name)")
}

이렇게 하면 새로운 변수가 들어오는지 보고 있다가 변수가 set되자마자 person의 이름을 프린트한다.

비록 KVO가 순수한 Swift 코드는 아니지만 Apple의 자체 API로 작업할 때는 더 좋다고한다. 이들이 모두 objective-c로 되어있기 때문에 모두 @objcdynamic 이기 때문이다.


야곰님께 질문

willset, didset과 KVO의 정의 정도는 알았지만, 두개의 쓰임에 있어서는 단순히 observe를 쓰냐 안쓰냐의 차이 정도로만 두개의 차이점을 생각했었는데, 나의 질문에 야곰님이 정말로 무릎을 탁 치게 만드는 답변을 내놓아주셨다. 내 질문은 단순히 위와 같다. willset과 didset 그리고 KVO는 서로 비슷한 역할을 하는 것인것 같은데 그 두개의 차이는 단순히 observe를 쓰냐 안쓰냐의 차이인걸까요?

답변

우리가 타입을 만드는 경우에는 자유롭게 willSet, didSet 등을 구현해줄 수 있겠지만, 다른 사람 혹은 외부 라이브러리에서 정의한 타입이라면 내부 소스를 마음대로 변경할 수 없겠죠.

그럴 때는 그 타입의 프로퍼티 등의 값이 변경되는지 KVO 방식으로 변화를 관찰할 수 있겠습니다.

즉, KVO는 상속 혹은 코드의 변경 없이 옵저빙을 할 수 있는 방법이겠다는 결론이네요 🙂

대………..박!