[iOS] KVC(Key Value Coding) 알아보기

안녕하세요. 이번에 준비한 자료는 KVC(Key Value Coding) 으로 Property 접근을 문자열로 통해 접근하는 방식을 말합니다. KVO(key Value Observing) 을 사용 하기 위해서는 위의 KVC 규약으로 사용하여야 Property의 변화된 값을 Notification을 받을 수 있습니다.

두 기능 다 iOS 2.0 부터 지원되는 기능으로 오래된 기능이지만, 사용하지 않아도 실시간으로 값을 받는거는 불가능하지 않다보니 크게 신경쓰고 못하고 있었습니다.
하지만 프로젝트를 진행함으로써 하나의 키를 바라보는데, 무분별한 Notification을 줄이고, 해당 Property의 값이 변하는걸 확실하게 체크하고자 KVC, KVO를 정리하려고 합니다.

현재 정리기준은 Objective C 부터 할 예정이며, Swift 4.x 버전부터 KVC의 스타일이 바뀌어 추가적으로 한번더 Swift 4.x 버전대로 정리하도록 하겠습니다.

Sample App : http://github.com/kimjiwook/KVCDemo

KVC

우선 KVC 사용 방법을 간단한 예제를 통해 설명해 보도록 하겠습니다.
지금까지 우리가 name 이라는 Property 의 접근하여 사용하는 방법으로는

1
2
self.name = @“KimJw”;
_name = @“KimJw”;
cs

이렇게 두가지 방법을 통해 가장 많이 사용하고 있을 것입니다.

이제 위의 접근 방법을 KVC 형식으로 변경해 보도록 하겠습니다.
1
[self setValue:@“KimJw” forKey:@“name”];
cs

추가적으로 KVC방법으로 값을 추출하는 방식입니다.
1
NSLog(@“%@“, [self valueForKey:@“name”]);
cs

앞서 간단한 예제로 보시면 어렵지 않게 접근 할 수 있습니다.  이제부터는 객체의 방식으로 접근하는 방법을 예제를 통해 진행해보도록 하겠습니다.
예제는 ViewDidLoad 메소드에서 간략하게 진행될 예정이며, 사용방법 중점으로 보시면 좋을 것 같습니다.

우선 예제에 앞서 Class 를 생성하도록 하겠습니다.

KVCObject.h
1
2
3
4
5
6
@interface KVCObject : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic) NSInteger age;
@end
cs

KVCObject.m
1
2
3
4
5
6
7
8
9
10
11
12
13
#import "KVCObject.h"
@implementation KVCObject
- (instancetype)init {
    self = [super init];
    if (self) {
        self.name = @"";
        self.age = 0;
    }
    return self;
}
@end
cs

Sample ViewController 를 생성하고 KVCObject를 Import 한 생태로 진행하겠습니다.

ViewController.m
1
2
3
4
5
6
7
8
9
#import "KVCObject.h"
@interface ViewController ()
@property (nonatomic, strong) KVCObject *kvcObject1;
@property (nonatomic, strong) KVCObject *kvcObject2;
@property (nonatomic, strong) KVCObject *kvcObject3;
@end
cs

1
2
3
4
5
    // 1. 기본적으로 사용하고 있는 방식.
    self.kvcObject1 = [[KVCObject allocinit];
    self.kvcObject1.name = @"KimJw";
    self.kvcObject1.age = 31;
    NSLog(@"%@, %ld",self.kvcObject1.name, self.kvcObject1.age);
cs

위와같이 작성하고 NSLog를 통해 결과를 확인하면, 역시나 예상했던 결과를 얻을 수 있습니다.

1
2
// 예상된 결과값
// KimJw, 31
cs

이번에는 KVC(Key Value Coding)

1
2
3
4
5
6
7
    // 2. KVC 방식
    [self.kvcObject1 setValue:@"KimJw(KVC)" forKey:@"name"];
    [self.kvcObject1 setValue:[NSNumber numberWithInteger:31] forKey:@"age"];
    NSString *kvcObject1Name = [self.kvcObject1 valueForKey:@"name"];
    NSInteger kvcObject1Age = [[self.kvcObject1 valueForKey:@"age"] integerValue];
    NSLog(@"%@, %ld", kvcObject1Name, kvcObject1Age);


이렇게 작성해서 결과를 보면,  우리가 사용하고 있는 Property 와 동일하게 결과값이 출력됩니다.

1
2
// 예상된 결과값
// KimJw(KVC), 31
cs

여기까지는 방식만 달라졌을뿐 사용하는데는 지장이없어 보입니다. 그런데 @“name” 이라는 String을 틀리게 썼을때 어떻게 되는지 알아보겠습니다.

1
2
3
// 2-1. 문제점 발생시키기
// key 값이 지정된 프로퍼티가 아닌경우에는 name -> namee 키값을 틀려봄.
[self.kvcObject1 setValue:@"KimJw(KVC) Error" forKey:@"namee"];
cs

위와같이 @“name” 의 Property 키값을 @“namee” 으로 변경해서 실행해보면, 아래와같이 앱이 죽는 현상이 발생합니다.

1
2
//    *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key namee.'
// 앱 크러시 발생 (키값을 매우 조심해야함.)
cs

앱을 운영하고 있는 입장에서 단순 String 실수로 인해 앱이 죽는다면, 심사기다리는 시간이 지옥일 것 같습니다. 이와 같이 KVC 방식으로 코드를 작성할때는 Key 값 Property 를 조심해서 사용해야합니다.

KeyPath

이번에는 조금더 알아보는 KVC를 한단계 더 들어가서 keypath 사용하는 방법 및 예제로 설명해 보도록 하겠습니다.

1
2
3
4
5
6
7
8
9
@interface KVCObject : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic) NSInteger age;
// 객체를 추가하도록 하겠습니다.
@property (nonatomic, strong) KVCObject *kvcObject;
@end
cs

1
2
3
4
5
6
7
8
9
10
11
// 1. 객체 내 객체 접근방식
    self.kvcObject2 = [[KVCObject allocinit];
    [self.kvcObject2 setValue:@"ParkSJ" forKey:@"name"];
    [self.kvcObject2 setValue:[NSNumber numberWithInteger:28] forKey:@"age"];
    self.kvcObject2.kvcObject = [[KVCObject allocinit];
    [self.kvcObject2 setValue:@"Ari" forKeyPath:@"kvcObject.name"];
    [self.kvcObject2 setValue:[NSNumber numberWithInteger:1] forKeyPath:@"kvcObject.age"];
    NSLog(@"%@, %ld", self.kvcObject2.kvcObject.name, self.kvcObject2.kvcObject.age);
cs

이제는 예상되는 결과값을 출력되는 것 을 확인할 수 있습니다.
1
2
// 예상된 결과값.
// Ari, 1
cs

한가지 더 객체 내 객체의 객체까지 접근해 보도록 하겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
    // 2. 객체 내 객체 내 객체 접근방식
    self.kvcObject3 = [[KVCObject allocinit];
    self.kvcObject3.kvcObject = [[KVCObject allocinit];
    self.kvcObject3.kvcObject.kvcObject = [[KVCObject allocinit];
    [self.kvcObject3 setValue:@"LeeHN" forKeyPath:@"kvcObject.kvcObject.name"];
    [self.kvcObject3 setValue:[NSNumber numberWithInteger:31] forKeyPath:@"kvcObject.kvcObject.age"];
    NSLog(@"%@, %ld", self.kvcObject3.kvcObject.kvcObject.name, self.kvcObject3.kvcObject.kvcObject.age);
    // 예상된 결과값.
    // LeeHN, 2
cs

이와 같이 하나의 Property 와 객체의 Property KeyPath 예제까지 알아봤습니다.
KVO 예제는 준비되는 대로 바로 포스팅 하도록하겠습니다. 감사합니다.

댓글