티스토리 뷰

Mobile/iOS

[iOS/Objective-C] KVO

out of coding 2017. 12. 31. 00:31

KVO라는 개념을 사용하면 swift에서는 reactive와 거의 흡사한 코드를 만들수 있습니다.

오늘은 Objective-C에서 사용하는 방법에 대해서 알아보도록 하고, Block으로 코딩을 하여 둔 라이브러리를 소개하려합니다.


일단 KVO란 Key Value Observing 입니다.

무엇이냐면 Key의 Value가 변경되는것을 감시하여서 코딩을 하는것이라고 생각하면됩니다.


일반적인 옵저버 패턴이라고 생각하면 되는데, 우리가 리스너를 만들어서 사용하던 방식을 그냥 사용할수 있도록 지원해주는 것입니다.

하나의 단점은 키를 String으로 넣어서 사용하여서 변수명을 고치거나 할 경우에는 이 부분들도 같이 수정을 하여 주어야 합니다.


그럼 코드로 설명하겠습니다.


1. 선언


1
2
3
4
5
6
7
8
9
10
11
    [self.child1 addObserver:self forKeyPath:@"name"
                     options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
                     context:nil];
    
    [self.child1 addObserver:self forKeyPath:@"age"
                     options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
                     context:nil];
    
    [self.child1 addObserver:self forKeyPath:@"address"
                     options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
                     context:nil];
cs


이렇게 선언을 하게 되면, self의 child1 변수의 name, age, address 값이 변경되는것을 감시하게 되며, 새로운 값과 이전의 값을 함께 알게 됩니다.


2. 해제


1
2
3
[self.child1 removeObserver:self forKeyPath:@"name"];
[self.child1 removeObserver:self forKeyPath:@"age"];
[self.child1 removeObserver:self forKeyPath:@"address"];
cs


선언된 키의 감시를 중지하게 됩니다.


3. 값의 변화


저의 경우에는 private 하게 변수를 선언하여서 사용을 하였습니다. 그래서 KVC의 setValue를 이용하여 값을 변경하여 주고 이것을 감시합니다.


1
2
3
4
[self.child1 setValue:@"DH" forKey:@"name"];
[self.child1 setValue:[NSNumber numberWithInteger:1] forKey:@"age"];
[self.child1 setValue:[NSNumber numberWithInteger:2] forKey:@"age"];
[self.child1 setValue:@"지구" forKey:@"address"];
cs


4. 값의 감시


1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"name"]) {
        NSLog(@"name = %@", change);
    }
    
    if ([keyPath isEqualToString:@"age"]) {
        NSLog(@"age = %@", change);
    }
    
    if ([keyPath isEqualToString:@"address"]) {
        NSLog(@"address = %@", change);
    }
}
cs


위의 observeValueForKeyPath: 는 제공하여 주는 메소드입니다. 그냥 사용하면 됩니다.


5. 결과


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2017-12-31 00:14:41.163402+0900 KVOExample[2901:441373] name = {
    kind = 1;
    new = DH;
    old = "";
}
2017-12-31 00:14:41.163670+0900 KVOExample[2901:441373] age = {
    kind = 1;
    new = 1;
    old = 0;
}
2017-12-31 00:14:41.163843+0900 KVOExample[2901:441373] age = {
    kind = 1;
    new = 2;
    old = 1;
}
cs


결과는 위와 같은데 무언가 이상하지 않나요? 분명 address의 값을 변경하여 주고, 이것을 감시하고 있는데 값이 변경된것이 전달되지 않습니다.

그 이유는 값이 변경된것을 통보할지 안할지를 조정할수 있기 때문입니다.


6. 결과를 감시 못하게


Children 파일의 내부입니다.


1
2
3
4
5
6
7
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
    if ([key isEqualToString:@"address"]) {
        return NO;
    } else {
        return [super automaticallyNotifiesObserversForKey:key];
    }
}
cs


address의 경우에는 사용하지 못하도록 하였기 때문에 당연스럽게 감시가 되지 않습니다.


7. 요점


이것을 사용하면 좋은점은 다음과 같을거 같습니다.


- 값의 변화에 대해서 리스너를 등록하여서 프로그래밍을 하여야 할 경우에 유용하다.

- private 한 값들을 조정하여서 개발이 가능하다.


단점은 다음과 같을것 같습니다.


- key에 string을 넣어주어야해서 변수명이 변경되었을 경우에 함께 변화하여야 한다.


8. 응용


iOS의 화면을 회전시키는 Method는 사용을 할수가 없습니다. private하기 때문입니다.

그런데 이것을 위의 KVC를 사용하면 가능합니다.


1
2
NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];
cs


물론... 애플에서 이것을 강제로 돌리는것을 반려를 낼수도 있기 때문에 개인적으로 생각하고 처리하기 바랍니다.

저는 반려나지 않았습니다. ㅎㅎ


마지막으로 해당 프로젝트를 첨부해드리니, 한번씩 돌려보면 느낌을 알수 있을것 같네요.


소스


KVOExample.zip


관련 링크


http://blog.andymatuschak.org/post/156229939/kvo-blocks-block-callbacks-for-cocoa-observers

https://gist.github.com/andymatuschak/153676


댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
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
글 보관함