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

단축키

Prev이전 문서

Next다음 문서

+ - Up Down Comment Print Files
?

단축키

Prev이전 문서

Next다음 문서

+ - Up Down Comment Print Files
Chapter 8 NSArrayController

Cocoa OOP에서 가장 보편적인 디자인패턴은 MVC이다. 이 디자인패턴에서 모든 클래스는 아래 그룹중에 하나에 속한다.

1. Model 클래스: 데이터를 나타낸다. 예를 들면, 은행시스템을 작성할 경우 SavingsAccount 이라는 모델 클래스를 생성하고 데이터로 처리내역과 현재의 잔액을 가지도록 한다. 데이터만 있다. 가장 좋은 모델 클래스는 사용자 인터페이스에 대한 내용이 없고 여러 app에서도 사용이 가능한 것이다.

2. View 클래스: GUI 부분이다. 예를 들면, NSSlider가 뷰 클래스이다. 가장 좋은 뷰 클래스는 일반적인 목적의 클래스이며 여러 app에서도 사용이 가능한 것이다.

3. Controller 클래스: app에 특화되고 app의 흐름을 제어하는 역할을 하는 클래스이다. 사용자는 데이터를 보려면 contoller 객체가 파일이나 DB에서 모델을 읽고 뷰 클래스를 사용해 출력한다. 사용자가 데이터를 변경하려면 뷰 객체가 contoller에게 통보하고 contoller는 모델 객체를 업데이트한다. 또한 데이터를 파일시스템 또는 DB에 저장한다.

MAC OS X 10.3 이전에는 데이터를 뷰와 모델사이에 넘기는데 많은 단순한 코드를 작성했었다. contoller 클래스의 단순한 정렬에는 코딩하는 것이 더 쉽다. (애플이 OX X 10.3 이후부터 NSContoller와 바인딩을 도입)

NSController는 추상클래스이고 NSObjectController는 이 추상클래스를 상속하는 하위클래스로 객체의 정보 또는 내용을 출력한다. NSArrayController는 데이터 객체의 내용을 배열형태로 가지고 있는 컨트롤러이다. 여기서는 이 NSArrayController를 사용한다.

01.png

** RaiseMan Application 제작하기

1. 개요
: 여기서는 고용인과 월급인상율을 기록하는 application을 작성하도록 한다. 완성된 형태는 아래와 같다.
02.png

만약 경험이 있는 코코아 프로그래머라면 이 애플리케이션을 Core Data를 사용해 만들 수 있다는 것을 알 수 있다. 여기서는 어떻게 동작하는 것을 보여주는 것이 목적이므로 메뉴얼하게 작성하도록 한다.

2. 프로젝트 생성 및 객체 다이어그램
03.png
* enable Create Document-Based Application: 여러 문서를 동시에 오픈할 수 있는 application
* 객체 다이어그램은 아래와 같다
04.png

프로젝트를 생성하면 아래와 같이 NSDocument의 하위클래스인 RMDocument 클래스가 이미 자동으로 작성된 것을 볼 수 있다. 문서객체는 파일을 읽고 쓰는 것을 담당한다. 여기서는 NSArrayController와 바인딩을 사용해 단순한 인터페이스를 구성하도록 한다.
05.png

2. Person 클래스 (모델 클래스)를 파일로 생성하고 코딩

메뉴에서 File -> New -> New File > Objective-C class 선택

06.png

07.png

Person.h 에서 아래와 같이 인스턴스 변수 및 원시타입 변수와 그에 대한 properties를 선언
#import <Foundation/Foundation.h>

@interface Person : NSObject {
    NSString *personName;
    float expectedRaise;
}
@property (readwrite, copy) NSString *personName;
@property (readwrite) float expectedRaise;
@end


Person.m 에서 init로 위에서 선언된 변수를 초기화하고 properties를 synthesize
(properties/synthesize 지시자 사용으로 accessor 메소드 호출가능)
#import "Person.h"

@implementation Person

@synthesize personName;
@synthesize expectedRaise;

- (id)init
{
    self = [super init];
    if (self) {
        expectedRaise = 0.05;
        personName = @"New Person";
    }
    return self;
}
@end


참고로 Person은 모델클래스이다. 즉, 사용자 인터페이스에 대한 정보를 가지고 있지 않으며, 이는 Cocoa framework 모두를 알 필요가 없다는 말이된다. 따라서 여기서는 Cocoa/Cocoa.h를 import하는 대신 Foundation/Foundation.h를 import 했다. 두 가지 모두 사용이 가능하지만 후자가 더 스타일리쉬하며 이 클래스는 커맨드라인 툴이나 iOS app에서도 사용이 가능하다.

3. RMDocument.h 및 .m에서 employees array 추가하기 (이 array는 후에 Person 클래스의 인스턴스들을 포함)

RMDocument.h 에서 employees array 선언한 후 setter 메소드를 수동으로 작성 (properties/synthesize 사용안함)
@interface RMDocument : NSDocument {
    NSMutableArray *employees;
}
- (void)setEmployees:(NSMutableArray *)a;
@end


RMDocument.m 에서 init 메소드에 employees 초기화 및 setter 메소드 구현
- (id)init
{
    self = [super init];
    if (self) {
        employees = [[NSMutableArray alloc] init];
    }
    return self;
}

- (void)setEmployees:(NSMutableArray *)a {
    // 두 포인터의 참조값이 동일한지를 체크하여 동일하면 무시하고 틀리면 employees에 인수 a를 대입
    if (a == employees)
        return;
    employees = a;
}


4. RMDocument.xib 설정

윈도우에서 'Your document contents here' 삭제한 후 table view와 두개의 button을 드래그앤드랍한다. table view에서 Attributes Inspector를 사용해 Content Mode를 Cell Based로 설정한다.
08.png

5. Array Contoller 설정

Cocoa->Objects & Controllers에서 Array Controller를 아래와 같이 드래그앤드랍하여 IB dock에 Array Controller 아이콘을 생성한다.
09.png

Array Contoller를 아이콘을 선택한 후 우측의 Attributes Inspector에서 Class 이름을 Person으로하고 personName, expectedRaise 키를 추가한다.
10.png

Bindings Inspector에서 Content Array를 확장한 후 Bind to:를 File's Owner로 변경하고 Model Key Path를 employees로 설정한다.
11.png

6. table view 컬럼을 바인딩

이제 array contoller의 내용을 출력하기 위해 table view 컬럼을 바인딩한다. 6장에서는 table view에 내용을 출력하기 위해 NSTableViewDataSource 프로토콜을 사용하였으나 바인딩을 사용하므로서 이 과정을 생략할 수 있다.

table view의 첫번째 컬럼을 선택하고 Bindings Inspector > Value에서 Bind to에 체크한 후 Array Controller를 선택한다. Contoller key는 arrangedObjects 그리고 Model Key Path는 personName로 설정한다. 이 설정을 cocoa에서는 컬럼값을 array controller의 personName과 arrangeObjects로 바인딩했다고 말한다.
12.png

두번째 컬럼은 예상 임금상승율을 출력한다. Library에서 Number Formatter를 찾은 후 (Library->Cocoa->Controls) 두번째 컬럼의 셀로 드래그앤드랍한다.
13.png

number formatter가 선택된 상태에서 Inspector을 선택한 후 formatter를 아래와 같이 설정한다.
14.png

바인딩 설정을 위해 두번째 컬럼을 선택한 후 첫번째 컬럼에서 한 것처럼 Bindings Inspector에서 컬럼값을 Array Controller의 arrangedObjects의 expectedRaise로 설정한다.
15.png

7. 버튼 객체의 target 설정 및 바인딩
- Add Employee 버튼 객체를 array controller로 ctrl-drag한 후 add:를 선택하여 버튼의 target을 설정한다. 
16.png

- 위와 동일하게 Remove 버튼을 ctrl-drag한 후 remove:를 선택하여 버튼의 target을 설정
17.png

array controller 에서 마우스 오른쪽 버튼을 클릭하면 아래와 같이 recieved action에 add:와 remove:가 추가된것을 볼 수 있다.
18.png

- Remove 버튼의 바인딩을 위해 bindings inspector에서 버튼의 Enabled를 Array Controller의 canRemove로 설정
19.png

추가로 사용자가 employee를 선택한 후 키보드의 delete키로 지울 수 있도록 Remove 버튼의 attributes inspector에서 Key Equivalent를 Delete키로 설정한다. (Key Equivalent의 text field에서 Del키를 누르면 된다)
20.png

8. 빌드 및 실행
버튼으로 Person 객체를 생성 및 삭제가 가능하고 텍스트 필드에서 편집도 가능하다.
21.png


** Key-Value Coding and nil

지금까지 IB의 각 컬럼에 어떤것들이 출력되는지 설명하였으나 정작 Person 클래스의 객체의 호출하는 accessor 메소드에 대한 코드는 작성하지 않았다. 어떻게 이것이 가능한가? 답은 Key-Value Coding이다. Key-Value Coding은 NSArrayController 같은 클래스가 일반화되고 재사용될 수 있게 만들어준다.

key-value coding 메소드는 타입을 마음대로 변경할 수 있다. 예를 들면, 사용자가 expected raise를 입력하면 formatter는 NSNumber의 인스턴스를 생성한다. 다음 setExpectedRaise:가 호출되는데, 이 메소드를 호출하기 전에 key-value coding 메소드인 setValue:forKey:가 자동으로 이 NSNumber 인스턴스를 float 타입으로 변경한다. 이 일련의 동작들은 개발자에게 상당히 편리하다.

한가지 문제가 있다면 NSDecimalNumber * 를 float를 변경할 때이다. 포인터는 nil이 될 수 있지만 원시타입은 float은 그렇지 않다. 만약 setValue:forKey: 메소드로 nil값이 인수로 들어오면 아래의 메소드가 호출된다.

- (void)setNilValueForKey:(NSString *)s

NSObject에서 정의된 이 메소드는 예외를 던진다. 따라서 사용자가 expected raise를 공란으로 남겨두면 app실행시 생성된 객체는 예외를 던지게 된다. 
22.png

이를 방지하기 위해 일번적으로 setNilValueForKey:를 오버라이드하여 기본값을 설정한다. 여기서는 Person 클래스에서 이 메소드를 오버라이드하고 expectedRaise의 기본값을 0.0으로 한다.

.m 파일에서 아래의 메소드를 추가한다.
- (void)setNilValueForKey:(NSString *)key 
{
	if ([key isEqual:@"expectedRaise"]) { 
		[self setExpectedRaise:0.0];
	} else {
		[super setNilValueForKey:key];
	} 
}



** 정렬기능 추가하기 (Sorting)

app 실행 후 컬럼헤더를 클릭하면 정렬이 되는 것을 확인할 수 있다. compare: 메소드가 정렬에 사용되며 대소문자를 구분한다. 그러므로 Z 다음에 a 가 나오는 것을 볼 수 있다.
23.png

여기서는 대소문자를 구분하지 않도록 설정한다. RMDocument.xib에서 각 컬럼의 Attributes Inspector에 있는 정렬기준을 설정한다.
테이블 컬럼을 선택한 후 inspector에서 아래와 같이 attributes를 설정한다.

Sort Key: personName
Selector: caseInsensitiveCompare:

24.png

아래와 같이 대소문자를 구분하지 않고 정렬된 것을 볼 수 있다.
25.png


** 참고: NSArrayController 없이 정렬하기

6장에서는 NSArrayController을 사용하지 않고 dataSource를 가지고 테이블의 값이 출력되었다. 이 경우 어떻게 정렬은 어떻게 구현하는가?

테이블의 컬럼에 들어가는 정보들은 NSSortDescriptor 객체의 배열에 들어가게 된다. sort Descriptor는 key, selector 및 데이터가 올림인지 내림차순으로 정렬하는지에 대한 정보를 포함하고 있다. NSMutableArray 객체가 있을 경우 아래의 메소드를 사용해 정렬할 수 있다.
- (void)sortUsingDescriptors:(NSArray *)sortDescriptors


An optional table-view dataSource method is triggered when the user clicks on the header of a column with a sort descriptor:
- (void)tableView:(NSTableView *)tableView sortDescriptorsDidChange:(NSArray *)oldDescriptors


Thus, if you have a mutable array that holds the information for a table view, you can implement the method like this:
- (void)tableView:(NSTableView *)tableView sortDescriptorsDidChange:(NSArray *)oldDescriptors
{
	NSArray *newDescriptors = [tableView sortDescriptors]; 
	[myArray sortUsingDescriptors:newDescriptors]; 
	[tableView reloadData];
}




List of Articles
번호 제목 글쓴이 날짜 조회 수
» Cocoa Programming 정리 08 - NSArrayController file Hojung 2013.02.25 5993
20 Cocoa Programming 정리 07 - Key-Value Coding 과 Key-Value Observing file Hojung 2013.02.20 5171
19 Cocoa Programming 정리 06 - 헬퍼 객체 file Hojung 2013.02.19 7568
18 Cocoa Programming 정리 05 - 타겟(Target)과 액션(Action) file Hojung 2013.02.14 6475
17 Cocoa Programming 정리 04 - 메모리 관리 file Hojung 2013.02.13 6372
16 Cocoa Programming 정리 03 - Objective-C file Hojung 2013.02.08 6136
15 Cocoa Programming 정리 02 - 시작하기 file Hojung 2013.02.05 8681
14 Cocoa Programming 정리 01 - Cocoa란? Hojung 2013.02.05 4866
13 객체 변수 선언시 @private 사용 Hojung 2012.09.18 4361
12 테스트 코드 작성 Hojung 2012.08.22 4104
11 NSString 객체의 생성과 변환 Hojung 2012.08.22 5166
10 클래스 프로퍼티 설정 Hojung 2012.08.22 4148
9 클래스와 객체 - 1 Hojung 2012.08.22 4236
8 Interface and Implementation Hojung 2012.08.22 3910
7 Extends vs Implements의 개념과 차이점 - 2 Hojung 2012.08.22 4319
6 Extends vs Implements의 개념과 차이점 - 1 Hojung 2012.08.22 4947
5 포인터 Hojung 2012.08.22 4214
4 조건문 및 순환문 Hojung 2012.08.22 4374
3 변수형 및 객체형 Hojung 2012.08.22 4360
2 hello world Hojung 2012.08.22 4182
Board Pagination ‹ Prev 1 2 Next ›
/ 2

Designed by sketchbooks.co.kr / sketchbook5 board skin

나눔글꼴 설치 안내


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

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

설치 취소

Sketchbook5, 스케치북5

Sketchbook5, 스케치북5

Sketchbook5, 스케치북5

Sketchbook5, 스케치북5