키-값 코딩과 키-값 감시(Key-Value Coding/Key-Value Observing)
개발 & SDK 2009/06/12 01:22* 키,값 코딩 (Key-Value Coding)
키 값 코딩은 NSObject 를 상속받는 모든 객체가 지원한다. 이는 NSKeyValueCoding 라는 프로토콜을 NSObject 가 따르고 있기 때문이다. 키 값 코딩은 무엇일까 ?
키 값 코딩 기술은 객체의 속성을 문자열을 통해서 간접적으로 접근하기 위한 것이다. 즉 객체의 속성에 접근하기 위해서 직접 객체의 접근자를 호출할 것이 아니라 속성의 이름을 인자로 전달해서 그 값에 접근하는 것이다. 객체에 접근할 때 내부적으로 접근자를 이용하게 된다. 만약 접근자가 존재하지 않으면 직접 멤버 변수에 접근한다. 다음 예제 코드를 보자.
01: - (void)applicationDidFinishLaunching:(UIApplication *)application {
02:
03: imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"test.png"]];
04: [window addSubview:imgView];
05: [window makeKeyAndVisible];
06:
07: //imgView.frame = CGRectMake(0,0,320,480);
08: [imgView setValue:[NSValue valueWithCGRect:CGRectMake(0,0,320,480)] forKey:@"frame"];
09:
10: //[imgView setAlpha:0.5];
11: [imgView setValue:[NSNumber numberWithFloat:0.5] forKey:@"alpha"];
12:
13: //imgView.superview.backgroundColor = [UIColor yellowColor];
14: [imgView setValue:[UIColor yellowColor] forKeyPath:@"superview.backgroundColor"];
15:
먼저 7,8 행을 보자. imgView 는 이미지 뷰의 객체이다. 뷰의 frame 속성을 변경하는 기존 방법은 7행과 같이 점 표현법으로 접근하거나, [imgView setFrame:] 과 같이 접근자를 호출하는 것이었다. 하지만 예제는 setValue:forKey 메서드를 이용해서 문자열로 속성값에 접근한다. 이러한 접근법을 키-값 코딩이라고 한다.
13,14행은 키-값에 대해서 경로를 사용한 것이다. 키-값 경로는 접근하려는 속성에 대해서 점 표현법 문자열을 쓰게 되면, 그 문자열을 분석해서 재귀적으로 키-값 코딩을 적용하는 것이다.
14행을 보면 imgView 의 superview 속성에 키-값 코딩으로 접근하고, 그 superview 객체에서 다시 키-값 코딩으로 backgroundColor 속성에 접근한다. 이와 같이 키-값 코딩과 키-값 경로를 이용한 방법은 점 표현법, 접근자와 밀접한 관계를 갖는다.
값을 읽는 방법도 마찬가지로 간단하다.
16: // 값 읽기
17: UIImage* image = [imgView valueForKey:@"image"];
18: UIColor* superBack = [imgView valueForKeyPath:@"superview.backgroundColor"];
19: NSLog(@"%@ %@",image,superBack);
20: }
코드의 17,18행에서 보는 것과 같이 valueForKey, valueForKeyPath 메서드를 이용해서 접근한다. 점 표현법을 사용하는 문자열로 값에 접근할 때는 반드시 valueForKeyPath 를 이용하는 것에 주의하자.
그렇다면 NSObject 가 키-값을 검색하는 방식은 어떻게 될까 ? NSObject 는 키-값 코딩으로 접근하는 메서드가 호출되면 그 키와 일치하는 이름의 접근자를 검색한다. 만약 접근자가 존재하지 않으면 직접 객체에서 일치하는 멤버 변수를 검색한다.
* 예외 상황
키-값 코딩으로 값을 읽으려고 할 때 키 값이 존재하지 않는 경우가 있다. 이럴때는 객체의 valueForUnderfinedKey 메서드가 호출된다. 이 메서드는 기본적으로 NSUndefinedKeyException 예외를 발생시킨다. 만약 존재하지 않는 키를 접근하려고 할 때 자신만의 동작을 정의하고 싶다면 valueForUndefinedKey 메서드를 재정의 하면 된다.
마찬가지로 값을 쓰는 경우도 해당 키가 존재하지 않는다면 setValue:forUndefinedKey: 메서드가 호출된다. 이 메서드도 기본 구현은 NSUndefinedKeyException 예외를 발생시킨다. 예외 대신에 별도의 작업을 진행하려면 setValue:forUndefinedKey 를 재정의 하자.
* 키-값 감시 (Key-Value Observing)
키-값 감시 기능은 객체의 프로퍼티가 변경되는 상황을 감시할 수 있다. 통보와 비슷하게 자신이 감시하고 싶은 키 경로를 지정해서 옵져버로 등록해 두면, 해당 키 경로의 값이 수정되었을 때 메서드를 호출해 준다. 키 경로를 감시하는 코드는 다음과 같다.
01: [objectA addObserver:self
02: forKeyPath:@"enable"
03: options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
04: context:NULL];
위 코드는 objectA 의 enable 이라는 프로퍼티 (키 경로)가 변경될 때를 감시하기 위한 것이다. 감시하는 객체는 self 이다. 그리고 3행에서는 옵션을 지정하게 되는데, 값이 변경되었다는 메서드가 호출될 때 딕셔너리로 전달될 정보를 결정한다. 마지막 4행의 context 는 값이 변경되었다는 메서드를 호출할때 같이 전달된다. 추가 정보를 인자로 전달할 때 사용한다.
위와 같이 코드를 수행하고, 코드의 어디선가 아래와 같이 속성을 변경한다고 하자.
[test setValue:[NSNumber numberWithBool:YES] forKey:@"enable"];
또는
test.enable = YES;
이렇게 키-값 코딩 이나 점 표현법을 이용해서 객체의 속성값을 변경하면 그 속성을 감시하고 있는 객체들의 다음 메서드를 호출해 준다.
01: - (void)observeValueForKeyPath:(NSString *)keyPath
02: ofObject:(id)object
03: change:(NSDictionary *)change
04: context:(void *)context
05: {
06: if ([keyPath isEqual:@"enable"]) {
07: for (NSString* val in [change allKeys]) {
08: NSLog(@"%@ : %@",val,[change objectForKey:val]);
09: }
10: }
11: }
전달되는 인자를 보자. 1행은 키 경로가 전달된다. 예제의 경우 “enable” 이 된다. 2행은 값이 변경된 객체가 전달된다. 그리고 4행은 감시자를 등록할 때 지정했던 context 값이 그대로 넘어온다. 이제 3행을 보자. 3행은 변경된 값에 대한 정보를 딕셔너리 형태로 담고 있다. 이 딕셔너리에 포함되는 정보는 다음과 같다.
• NSKeyValueChangeKindKey
• 값이 변경된 형태를 알려준다.
• NSKeyValueChangeNewKey
• 새로운 값
• NSKeyValueChangeOldKey
• 변경되기 전의 값
마지막으로 키-값 감시를 더이상 하지 않을 때는 제거해야 한다. 다음과 같이 간단하게 제거할 수 있다.
01: - (void)unregisterForChangeNotification
02: {
03: [objectA removeObserver:self forKeyPath:@"enable"];
04: }
* 수동으로 알리기
지금까지 배운 것처럼, 어떤 속성에 대해서 접근자(점 표현법이나 키-값 코딩) 를 이용해서 값을 변경하면 그 내용이 자동으로 감시자에게 통보된다. 하지만 객체의 내부에서 수동으로 통보할 수도 있다.
01:- (void) foo {
02: [self willChangeValueForKey:@"enable"];
03: _enable = YES;
04: ...
05: [self didChangeValueForKey:@"enable"];
06: }
2행과 5행에서 처럼 값이 곧 변경될것이다, 값이 변경되었다는 것을 알리기 위한 메서드를 호출해 주면 된다.
'개발 & SDK' 카테고리의 다른 글
| iPhone SDK 메모리 관리 기법 (2) | 2009/06/16 |
|---|---|
| 쓰레드 기초 (0) | 2009/06/15 |
| 키-값 코딩과 키-값 감시(Key-Value Coding/Key-Value Observing) (0) | 2009/06/12 |
| 프로퍼티와 접근자(Accessor) (0) | 2009/06/10 |
| Notification (통보) 사용하기 (3) | 2009/06/08 |
| iPhone 어플리케이션의 실행 구조 (0) | 2009/04/26 |

