본문 바로가기
조회 수 4630 추천 수 0 댓글 0
?

단축키

Prev이전 문서

Next다음 문서

+ - Up Down Comment Print Files
?

단축키

Prev이전 문서

Next다음 문서

+ - Up Down Comment Print Files
Chapter 7 Key-Value Coding and Key-Value Observing

Key-value coding (or KVC)이란 변수 이름으로 값을 설정하거나 얻는 메커니즘이다. 이름은 단지 문자열에 불과하지만 그것을 키로서 사용할 수 있다. 예를 들면, 아래와 같이 Student라는 클래스가 있고 NSString타입의 firstName 인스턴스가 있다.
@interface Student : NSObject
{
	NSString *firstName; 
}
... 
@ends


Student 타입의 인스턴스를 생성하고 firstName을 설정(setting)하려면 아래와 같은 코드가 필요하다.
Student *s = [[Student alloc] init]; // Student 클래스의 인스턴스 초기화
[s setValue:@"Larry" forKey:@"firstName"]; // 참조변수 s의 메소드 호출하여 firstName을 설정


firstName은 아래와 같은 메소드로 읽을 수 있다. (getting)
NSString *x = [s valueForKey:@"firstName"];


참고로 위 두 메소드는 NSObject에 정의되어 있으므로 모든 객체에서 사용할 수 있다.


** Key-Value Coding

1. 새로운 프로젝트 KvcFun 생성하고 .h 파일에 int 타입의 인스턴스 변수 fido 추가
#import <Cocoa/Cocoa.h>
@interface KvcFunAppDelegate : NSObject <NSApplicationDelegate>
{
    int fido;
}
@property (assign) IBOutlet NSWindow *window;
@end


2. .m 파일에서 init 메소드를 생성하고 key-value coding을 사용해 fido를 설정하고 읽을 수 있도록 한다. 여기서 주의할 점은 key-value coding은 객체를 다룬다. 그러므로 원시값을 다루어야 하는 경우 int 값을 전달하지 말고 NSNumber 객체를 생성한 후 전달해야 한다.
- (id)init
{
    self = [super init];
    if (self) {
        
        // setValue:forKey: 메소드로 fido 인스턴스 값을 설정
        // NSNumber의 클래스메소드인 numberWithInt를 호출하여 int 5를 객체로 변환하여 인수로 사용
        [self setValue:[NSNumber numberWithInt:5] forKey:@"fido"];
        
        // valueForKey 메소드를 호출하여 fido의 값을 얻음
        NSNumber *n = [self valueForKey:@"fido"];
        
        // n은 객체이므로 포맷으로 %@ 사용
        NSLog(@"fido = %@", n);
    }
    return self;
}


key-value coding 메커니즘은 fido에 값을 설정하기 전에 NSNumber 객체를 자동으로 int 형으로 변환한다.
빌드 및 실행하면 아래와 같은 로그가 출력된다.
01.png

만약 fido를 getting/setting 하기 위한 accessor 메소드(fido, setFido:)가 구현되어 있다면 key-value coding 메커니즘에 의해 그 메소드들이 사용된다. accessor 메소드는 이름이 중요하다. 이 경우 getter는 fido, setter는 setFido: 이다. 
.m 파일에 아래와 같이 accessor 메소드인 getter/setter 메소드를 추가한다.
- (int) fido
{
    NSLog(@"-fido is returning with %d",fido);
    return fido;
}

- (void) setFido:(int)x
{
    NSLog(@"-setFido: is called with %d", x);
    fido = x;
}


위 메소드를 .h에 선언한다.
- (int) fido;
- (void) setFido:(int) x;


빌드 및 실행을 하면 accessor 메소드가 출력되고 아래와 같은 로그가 출력된다.
02.png

즉, init 메소드에서 아래의 코드실행시 accessor 메소드인 setFido: 메소드가 호출되었고,
[self setValue:[NSNumber numberWithInt:5] forKey:@"fido"];
아래의 메소드 실행시 accessor 메소드인 fido 메소드가 호출되었다.
NSNumber *n = [self valueForKey:@"fido"];


** Bindings

cocoa의 많은 그래픽 객체들은 binding을 가지고 있다. fido같은 key를 그래픽 객체의 값 또는 font color 같은 attribute에 바인딩하면 뷰는 자동으로 fido키의 값과 attribute값을 싱크상태로 유지한다. 즉, 한쪽 같이 변경되면 다른 한쪽도 변경. 여기서는 slider를 추가하고 fido와 바인딩하여 key-value coding을 사용해 어떻게 싱크상태를 유지하는지 보여준다.

1. 아래와 같이 slider를 윈도우에 추가한 후 Attributes Inspector에서 slider Continuous를 만든다.
03.png

2. Bindings Inspector에서 slider의 값을 KvcFunAppDelegate 인스턴스의 fido키와 바인딩한다.
04.png

3. 빌드 및 실행하면 아래와 같이 최초 slider가 최초의 값을 얻는데 valueForKey:를 사용 (fido 메소드 호출됨). slider가 이동함에 따라 setValue:forKey:를 호출하여 fido의 값을 업데이트한다. (setFido: 메소드 호출됨)
05.png


** 키값 변경을 그래픽 객체로 반영하기

키 값을 변경하기 위해 accessor 또는 key-value coding을 사용할 때, 옵저버들이 자동으로 이 변경을 인식했다. 만약에 키값을 직접 변경한다면 어떻게 되는가?

1. action 메소드 선언 및 구현

.h 파일에서 새로운 action 메소드를 선언한다.
- (IBAction) incrementFido:(id)sender;

.m 파일에서 위 메소드를 구현한다.
- (IBAction) incrementFido:(id)sender
{
    fido++;
    NSLog(@"fido is now %d", fido);
}


2. 윈도우에서 버튼을 생성하고 "Increment Fido"로 이름을 붙인다. 다음 KvcFunAppDelegate 인스턴스로 control-drag한다. 이제 버튼을 누르면 incrementFido: action이 실행된다. (인터페이스 객체를 먼저 생성하고 연결해도 무방하다)
06.png

아래와 같이 delegate에 received action에 push button이 추가된것을 볼 수 있다.
07.png

빌드 및 실행한 후 버튼을 클릭하면 fido 값의 증가가 로그창에 보이나 slider에 반영되지 않은 것을 볼 수 있다.

3. 변수를 직접 변경하려면 옵저버의 notification을 명시적으로 트리거해야 한다. incrementFido: 메소드를 아래와 같이 변경한다.
- (IBAction)incrementFido:(id)sender
{
    [self willChangeValueForKey:@"fido"];
    fido++;
    NSLog(@"fido is now %d", fido);
    [self didChangeValueForKey:@"fido"];
}


이제 빌드 및 실행을 하면 제대로 동작하는 것을 볼 수 있다.
08.png

4. (옵션) 위의 incrementFido: 메소드를 변경하는 것 외에 2가지 솔루션이 더 있다.

첫번재: key-value coding 사용하기
- (IBAction)incrementFido:(id)sender
{
	NSNumber *n = [self valueForKey:@"fido"];
	NSNumber *npp = [NSNumber numberWithInt:[n intValue] + 1]; 
	[self setValue:npp forKey:@"fido"];
}


두번째: accessor 메소드 사용하기 (가장 간단명료함)
- (IBAction)incrementFido:(id)sender
{
	[self setFido:[self fido] + 1]; 
}


<참고>
위의 내용을 객체 다이어그램으로 표현하면 아래와 같다.
09.png


** Properties

assessor 호출시의 또다른 옵션은 . 표시법이다. 포인터가 rover인 경우 getter 메소드인 rex를 아래와 같은 방법으로 호출할 수 있다. 예를 드면,
NSLog(@"Rover's rex is %@", rover.rex);


setter인 setRox: 메소드를 호출하는 경우, 아래와 같은 방법을 사용할 수 있다.
rover.rex = [NSDate date];


OC 프로그래머는 각자의 취향에 따라 사용하나 여기서는 위 . 표시법을 사용하지 않기로 한다. 참고로 OC에서는 arrow operator도 가능하다. 즉, obj.name 대신 obj->name으로도 표현이 가능하다. 이 두방법을 이용할 경우 fido, setFido: 등의 getter/setter 메소드를 선언하고 구현하지 않아도 . 과 -> 표시법으로 accessor를 이용 객체 인스턴스 값을 얻거나 설정할 수 있다.

(참고)

여기서 . 표시법 또는 화살표 표시법을 사용하지 않는다면 각 인스턴스 변수마다 setter/getter 메소드를 매번 작성할것인가? 해답은 Properties에 있다. Properties를 사용하면 fido, setFido: 같은 accessor 메소드를 각 인스턴스 변수마다 작성하지 않아도 된다.

Properties 사용예

1. .h 에서 fido, setFido: 메소드 선언을 삭제하고 아래와 같이 property 선언을 붙여 생성한다.
@property (readwrite, assign) int fido;

위 하나의 라인이 fido, setFido: 메소드를 모두 선언하는 것과 동등하다.

2. .m 파일에서 @synthesize 을 이용 accessor 메소드를 구현한다. fido, setFido: 메소드를 삭제하고 아래의 코드를 넣는다.
@synthesize fido;


빌드 및 실행하면 결과가 지난번과 동일한 것을 알 수 있다.
@synthesize는 .h에서 선언된 fido를 위한 accessor 메소드를 구현하는 지시자이다.

-------------<참고: Attributes of a Property>---------------
attributes는 readwrite 또는 readonly를 포함할 수 있으며, 기본값은 readwrite이다. readonly는 setter 메소드가 없다. setter 메소드를 위해 assign, strong, weak, copy 등의 attributes가 있다.

• assign (the default) makes a simple assignment happen. This attribute is most commonly used for scalar, nonpointer types, such as integers and floating-point values.

• strong says that this property is a strong reference. It keeps the object being pointed to from being deallocated while this pointer is set. It is specific to ARC code; if you are not using ARC, the retain attribute is equivalent.

• weak denotes a weak reference. It is similar to assign, except that once the object being pointed to is deallocated, this property will be set to nil. It is supported only by code compiled with ARC.

• copy makes a copy of the new value and assigns the variable to the copy. This attribute is often used for properties that are strings and other classes with mutable subclasses.

Attributes can also include nonatomic. If your application is multithreaded, it is sometimes important that your setter methods be atomic. That is, the execution of the setter method from one thread will not conflict with the execution of the same setter method on another thread. By default, the @synthesize call will generate accessors with this property. This involves using a lock to ensure that only one thread at a time is executing the setter. Creating and using the locks introduces some overhead. If you know that the accessors for a property don’t need to be atomic, you can eliminate the overhead by adding nonatomic to the attributes.

If a property name exactly matches the corresponding instance variable name, you can simply @synthesize that name: 

@synthesize fido;

If, however, you prefer to use a prefix with your instance variables (Xcode likes to use an underscore prefix), you can specify the instance variable name by using this technique:

@synthesize fido = _fido;
--------------------- end of 참고 -------------------------

(정리)

1. .h 파일에서 인스턴스 변수를 선언하고 해당 변수에 접근하려면 .m 구현 파일에서 Key-value coding 메커니즘을 이용 해당 인스턴스를 초기화하고 특정 메소드로 setting/getting 한다.

.m 에서.....
Student *s = [[Student alloc] init]; // Student 클래스의 인스턴스 초기화
[s setValue:@"Larry" forKey:@"firstName"]; // 참조변수 s의 메소드 호출하여 firstName을 설정
NSString *x = [s valueForKey:@"firstName"];

2. .h 파일에서 인스턴스 변수를 위한 accessor 메소드가 선언되어 있다면 위의 setter/getter 메소드는 아래의 accessor 메소드들을 사용한다. 보통 fido, setFido: 메소드를 호출

.m 에서.....
- (int) fido
{
    return fido;
}

- (void) setFido:(int)x
{
    fido = x;
}

3. 위의 accessor 메소드 선언/구현없이 . 또는 -> 표시법으로 인스턴스 변수로 accessor를 이용 setting/getting이 가능하다. (사용유무는 프로그래머의 취향에 따라 틀리다)

obj.name
obj->name

4. .h 선언시 @property, .m 에서 @synthesize은 위의 accessor 메소드의 setter/getter 메소드 모두를 포함한 것과 동일하다. 위 지시문을 선언/구현하면 fido, setFido: 메소드 사용이 가능하다.

.h에서......
@property (readwrite, assign) int fido;

.m에서......
@synthesize fido;

즉, @property나 . 또는 화살표 표시법을 사용하면 인스턴스 객체 선언 후 setter/getter를 수동으로 작성하지 않아도 set/get을 할 수 있다. 위 두 방법 중 어느 것을 택할지 또는 모두 사용할지는 프로그래머의 취향에 달려있다.

List of Articles
번호 제목 글쓴이 날짜 조회 수
21 Cocoa Programming 정리 08 - NSArrayController file Hojung 2013.02.25 5505
» Cocoa Programming 정리 07 - Key-Value Coding 과 Key-Value Observing file Hojung 2013.02.20 4630
19 Cocoa Programming 정리 06 - 헬퍼 객체 file Hojung 2013.02.19 6979
18 Cocoa Programming 정리 05 - 타겟(Target)과 액션(Action) file Hojung 2013.02.14 5924
17 Cocoa Programming 정리 04 - 메모리 관리 file Hojung 2013.02.13 5792
16 Cocoa Programming 정리 03 - Objective-C file Hojung 2013.02.08 5582
15 Cocoa Programming 정리 02 - 시작하기 file Hojung 2013.02.05 8177
14 Cocoa Programming 정리 01 - Cocoa란? Hojung 2013.02.05 4398
13 객체 변수 선언시 @private 사용 Hojung 2012.09.18 3841
12 테스트 코드 작성 Hojung 2012.08.22 3590
11 NSString 객체의 생성과 변환 Hojung 2012.08.22 4640
10 클래스 프로퍼티 설정 Hojung 2012.08.22 3630
9 클래스와 객체 - 1 Hojung 2012.08.22 3717
8 Interface and Implementation Hojung 2012.08.22 3440
7 Extends vs Implements의 개념과 차이점 - 2 Hojung 2012.08.22 3803
6 Extends vs Implements의 개념과 차이점 - 1 Hojung 2012.08.22 4403
5 포인터 Hojung 2012.08.22 3727
4 조건문 및 순환문 Hojung 2012.08.22 3863
3 변수형 및 객체형 Hojung 2012.08.22 3841
2 hello world Hojung 2012.08.22 3645
Board Pagination ‹ Prev 1 2 Next ›
/ 2

Designed by sketchbooks.co.kr / sketchbook5 board skin

나눔글꼴 설치 안내


이 PC에는 나눔글꼴이 설치되어 있지 않습니다.

이 사이트를 나눔글꼴로 보기 위해서는
나눔글꼴을 설치해야 합니다.

설치 취소

Sketchbook5, 스케치북5

Sketchbook5, 스케치북5

Sketchbook5, 스케치북5

Sketchbook5, 스케치북5