[iOS] Objective-C의 KVC와 KVO 이해하기

1 분 소요

KVC(Key-Value Coding)와 KVO(Key-Value Observing)는 이름이 비슷하지만 역할은 다르다. KVC는 문자열 키로 객체의 값을 읽고 쓰는 방식이고, KVO는 KVC를 기반으로 속성의 변경을 관찰하는 방식이다.

KVC: 문자열 키로 속성에 접근하기

NSKeyValueCoding은 객체의 프로퍼티를 이름이나 키 경로로 간접 접근하는 메커니즘이다. NSObject가 기본 구현을 제공하므로 Cocoa 객체에서는 일반적으로 별도 구현 없이 사용할 수 있다.

@interface TestObject : NSObject
@property (nonatomic, copy) NSString *testValue;
@end

@implementation TestObject
@end

TestObject *object = [TestObject new];
[object setValue:@"hello" forKey:@"testValue"];

NSString *value = [object valueForKey:@"testValue"];
NSLog(@"%@", value); // hello

키 경로를 사용하면 중첩된 객체에도 접근할 수 있다.

NSString *city = [person valueForKeyPath:@"address.city"];

문자열 키는 컴파일러가 오타를 검사하지 못한다. 존재하지 않는 키를 사용하면 기본적으로 NSUndefinedKeyException이 발생하므로, 상수나 NSStringFromSelector()를 활용해 키를 한곳에서 관리하는 편이 안전하다.

KVO: 속성 변경 관찰하기

KVO는 다른 객체의 프로퍼티가 바뀌었을 때 알림을 받는 옵저버 패턴이다. 관찰 대상 프로퍼티가 KVO 호환 방식으로 변경되어야 하며, 일반적인 Objective-C 프로퍼티의 setter를 통한 변경은 자동으로 알림을 발생시킨다.

static void *TestValueContext = &TestValueContext;

[object addObserver:self
         forKeyPath:@"testValue"
            options:NSKeyValueObservingOptionOld |
                    NSKeyValueObservingOptionNew
            context:TestValueContext];

변경 알림은 observeValueForKeyPath:ofObject:change:context:에서 처리한다.

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSKeyValueChangeKey, id> *)change
                       context:(void *)context {
    if (context == TestValueContext) {
        id oldValue = change[NSKeyValueChangeOldKey];
        id newValue = change[NSKeyValueChangeNewKey];
        NSLog(@"testValue: %@ → %@", oldValue, newValue);
        return;
    }

    [super observeValueForKeyPath:keyPath
                         ofObject:object
                           change:change
                          context:context];
}

관찰이 끝나면 등록한 객체와 키 경로를 정확히 맞춰 해제한다.

[object removeObserver:self
            forKeyPath:@"testValue"
               context:TestValueContext];

옵저버의 수명과 해제 시점을 명확히 관리하지 않으면 크래시가 발생할 수 있다. 직접 인스턴스 변수에 값을 대입해 setter를 우회하거나, KVO 알림을 수동으로 비활성화한 경우에는 자동 알림이 발생하지 않을 수 있다.

KVO와 NotificationCenter의 차이

  • KVO: 특정 객체의 특정 프로퍼티 변경을 관찰할 때 적합하다.
  • NotificationCenter: 여러 객체가 하나의 의미 있는 이벤트를 구독할 때 적합하다.
  • Delegate/Closure: 일대일 통신이나 명시적인 흐름이 필요할 때 이해하기 쉽다.

KVO는 모델 객체의 상태 변화를 연결하는 데 유용하지만, 문자열 키와 객체 수명 관리 비용이 있다. 이벤트의 의미가 중요하다면 Notification이나 명시적인 콜백이 더 읽기 쉬울 수 있다.

참고 자료

댓글남기기