[iOS] KVO(Key Value Observing) 알아보기.

안녕하세요. KVC(Key Value Coding)에 이어 KVO(Key Value Observing)에 대해서 알아보도록 하겠습니다.

앞서 진행하기전 KVO를 사용하기 위해서는 KVC의 규약을 지켜야만 사용이 가능함으로 정리했던 내용 링크 공유 드리고, 진행하도록 하겠습니다. 
Sample App 또한 KVC 연장선으로 진행하겠습니다.


KVO

필자는 Notification과 같다는 관점으로 가지고 테스트를 하였으며, 아래와같은 시점으로 함수먼저 살펴보았다.

1. KVO 등록하기.
2. 실시간 받기.
3. KVO 해지하기.


1. KVO 등록하는 함수. (링크 : https://developer.apple.com/documentation/objectivec/nsobject/1412787-addobserver?language=objc)
1
2
3
4
- (void)addObserver:(NSObject *)observer 
         forKeyPath:(NSString *)keyPath 
            options:(NSKeyValueObservingOptions)options 
             context:(nullable void *)context;
cs

2. Observer 함수 구현 (링크 : https://developer.apple.com/documentation/objectivec/nsobject/1416553-observevalueforkeypath?language=objc)
1
2
3
4
- (void)observeValueForKeyPath:(NSString *)keyPath 
                      ofObject:(id)object 
                        change:(NSDictionary<NSKeyValueChangeKey,id> *)change 
                       context:(void *)context;
cs
1
2
- (void)removeObserver:(NSObject *)observer 
            forKeyPath:(NSString *)keyPath;
cs

위와 같이 사용되는 함수 이며, 기본적으로 사용되는 방식에 대해서 예제로 진행해 보았다.

1. KVO 기본적인 예제.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#import "KVOViewController.h"
#import "KVCObject.h"
@interface KVOViewController ()
@property (nonatomic, strong) KVCObject *kvcObject1;
@property (nonatomic, strong) KVCObject *kvcObject2;
@property (nonatomic, strong) KVCObject *kvcObject3;
@end
@implementation KVOViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"KVO ViewDidLoad=====================");
    
    // 생성자.
    self.kvcObject1 = [[KVCObject allocinit];
    
    // 1. KVO 등록하는 방법.
    [self.kvcObject1 addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    [self.kvcObject1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    
    // 값 변경해보기.
    [self.kvcObject1 setValue:@"KimJw(KVC)" forKey:@"name"];
    [self.kvcObject1 setValue:[NSNumber numberWithInteger:31] forKey:@"age"];
}
- (void) viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    NSLog(@"KVO ViewDidDisappear");
    // 2. KVO 해지하는 방법.
    [self.kvcObject1 removeObserver:self forKeyPath:@"name"];
    [self.kvcObject1 removeObserver:self forKeyPath:@"age"];
}
// 등록된 키값이 변경되면 알려주는 함수.
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    
    if ([@"name" isEqualToString:keyPath]) {
        NSLog(@"이름이 변경되었습니다. [%@]",change);
    }
    
    if ([@"age" isEqualToString:keyPath]) {
        NSLog(@"나이가 변경되었습니다. [%@]",change);
    }    
}
@end
cs

결과 출력값.

위와 같이 기본적인 방법으로도 구현이 가능하며, 추가적으로 KVO 등록시 Context사용 방법에 대해서도 샘플 소스 올립니다.

2. KVO Context 예제.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#import "KVOContextViewController.h"
#import "KVCObject.h"
// KVO Context 구분값 포인터정보.
static void *kvo1Context = &kvo1Context;
static void *kvo2Context = &kvo2Context;
@interface KVOContextViewController ()
@property (nonatomic, strong) KVCObject *kvcObject1;
@property (nonatomic, strong) KVCObject *kvcObject2;
@property (nonatomic, strong) KVCObject *kvcObject3;
@end
@implementation KVOContextViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"KVO ViewDidLoad=====================");
    
    // 생성자.
    self.kvcObject1 = [[KVCObject allocinit];
    
    // 1. KVO 등록하는 방법.
    [self.kvcObject1 addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:kvo1Context];
    [self.kvcObject1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:kvo1Context];
    
    // 값 변경해보기.
    [self.kvcObject1 setValue:@"KimJw(KVC)" forKey:@"name"];
    [self.kvcObject1 setValue:[NSNumber numberWithInteger:31] forKey:@"age"];
    
    // 두번쨰 값 등록.
    self.kvcObject2 = [[KVCObject allocinit];
    [self.kvcObject2 addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:kvo2Context];
    
    // 값 변경해보기.
    [self.kvcObject2 setValue:@"ParkSJ" forKey:@"name"];
}
- (void) viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    NSLog(@"KVO ViewDidDisappear");
    // 2. KVO 해지하는 방법.
    [self.kvcObject1 removeObserver:self forKeyPath:@"name"];
    [self.kvcObject1 removeObserver:self forKeyPath:@"age"];
    
    [self.kvcObject2 removeObserver:self forKeyPath:@"name"];
}
// 등록된 키값이 변경되면 알려주는 함수.
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    
    if (kvo1Context == context) {
        NSLog(@"KVO1 의 값임.");
        if ([@"name" isEqualToString:keyPath]) {
            NSLog(@"이름이 변경되었습니다. [%@]",change);
        }
        
        
        if ([@"age" isEqualToString:keyPath]) {
            NSLog(@"나이가 변경되었습니다. [%@]",change);
        }
    }
    
    if (kvo2Context == context) {
        NSLog(@"KVO2 의 값임.");
        if ([@"name" isEqualToString:keyPath]) {
            NSLog(@"이름이 변경되었습니다. [%@]",change);
        }
    }
    
}
@end
cs


결과값.

이와 같이 Context를 사용하면, 같은 객체라도 구분을 지을수 있게 구분을 할 수 있는 형식으로 사용이 가능합니다.


댓글