Low-Level CoreData Tutorial

OSXDEV

Jump to: navigation, 찾기

목차

[편집] Introduction to Low-Level Core Data Tutorial

이 튜토리얼은 당신을 아주 기본적인 Core Data기반의 명령어 입력 유틸리티로 인도한다. 목표는 Core Data 어플리케이션을 완전히 코드로만 작성하는 것을 보이는 것이다.

이 튜토리얼의 목표는 low-level의 Core Data기반 유틸리티를 만드는 것이다. 이것은 단순히 유틸리티가 동작한 날짜와 를 기록하고, 아웃풋으로 동작 히스토리를 프린트하는 것이다. 이것은 Cocoa 바인딩이 Core Data의 성능향상을 위해 사용될 수는 있어도, Core Data만을 독립적으로도 사용할 수 있다는 것이다. 게다가 이 예제는 Core Data 스택의 생성과 managed 오브젝트의 인스턴스화와 패칭의 모든 면모를 유저인터페이스로 인한 산만함 없이 보여준다. 모델의 생성까지 코드를 통해 보여줄 것이다.

[편집] Who Should Read This Document

Core Data 프레임웍을 이용해 유저 인터페이스 없는 유틸리티를 만들거나 Core Data의 하부구조를 깊이 이해하고자 하는 사람에게 유용한 튜토리얼이다.

[편집] Organization of This Document

"Overview of the Tutorial" 만들 유틸리티를 설명하고 과제 제한.

"Creating the Project" Xcode를 이용해 Foundation Tool을 만드는 법과 Core Data에서의 링크 법을 설명

"Creating the Managed Object Model" 유틸리티의 데이타 모델을 코드로 만드는 법 설명

"The Application Log Directory" 유틸리티의 퍼시스턴트 스토어를 위한 파일을 저장할 티렉토리를 확인하고 필요하다면 생성하는 법 설명

"Creating the Core Data Stack" managed 오브젝트 컨텍스트와 퍼피스턴트 스토어 코디네이터를 코드로 만들고 설정하기를 설명

"The Custom Managed Object Class" Run엔티티를 정의하고 커스텀 managed 오브젝트 클래스를 실행하는 법 설정.

"Listing Previous Runs" 퍼시스턴트 스토어로부터 Run인스턴스를 패치하는 법 묘사

[편집] See Also

Core Data에 대한 보다 나은 이해를 위해서 혹은 도큐멘트 기반의 어플리케이션에서 Core Data를 사용하길 원한다면 다음 문서들을 읽어야 할 것이다. Core Data Programming Guide 는 Core Data 프레임웍에 의해 제공되는 기능들을 하이레벨의 오버뷰에서부터 깊은 수준의 설명으로 묘사했다. NSPersistentDocument Core Data 은 Core Data를 이용해 Tutorial 도큐멘트 기반의 어플리케이션을 만드는 과정을 단계별로 보여준다.

[편집] Introduction to Low-Level Core Data Tutorial

이 튜토리얼은 아주 기본적인 Core Data기반의 명령어 입력 유틸리티를 만드는 길로 안내한다. 목표는 Core Data어플리케이션을 통째로 코드로 작성하는 것이다. 이것은 Cocoa 바인딩이 Core Data의 성능향상을 위해 사용될 수는 있어도, Core Data만을 독립적으로도 사용할 수 있다는 것이다. 게다가 이 예제는 Core Data 스택의 생성과 managed 오브젝트의 인스턴스화와 패칭의 모든 면모를 유저인터페이스로 인한 산만함 없이 보여준다. 모델의 생성까지 코드를 통해 보여줄 것이다.

[편집] Task Goal

이 튜토리얼의 목표는 low-level의 Core Data기반 유틸리티를 만드는 것이다. 이것은 단순히 유틸리티가 동작한 날짜와 를 기록하고, 아웃풋으로 동작 히스토리를 프린트하는 것이다. 유틸리티는 Run이라는 하나의 엔티티만을 사용한다. Run 엔티티는 매우 단순하다; processID와 프로세스가 실행된 날짜, 두개의 어트리뷰트를 가지고 있다.

이 튜토리얼에서 강조하는 것은 Core Data의 로-레벨 기능성이지 컴팩트함이나 유지보수성 혹은 사용자 친화성이 아니라는 것을 명심하라. 몇몇 심층적인 동작에 대한 설명들이 있겠지만 Core Data 하부구조의 깊은 분석을 주지는 않는다.

[편집] Creating the Project

튜토리얼의 이 파트는 CDCLI 프로젝트의 생성으로 인도한다.

[편집] Create a New Project

Core Date는 Cocoa 프레임워크에 통합되어 있으므로 모든 코코아 혹은 파운데이션 어플리케이션은 이것을 사용할 수 있다. 당신이 만들 CDCLI 어플리케이션은 Core Data를 이용하는 파운데이션 툴이다. 초기 프로젝트를 만들기 위해 다음 단계를 따라라.

1. 파일 메뉴에서 New Project를 선택

2. Xcode의 프로젝트 어시스턴트 윈도우에서 Foundation Tool선택(Command Line Utility 섹션에서)하고 Next버튼 클릭.

3. 프로젝트 이름(예를 들어"CDCLI")을 입력하고 프로젝트의 폴더를 선택. 어시스턴트에 의해 생성된 main함수를 포함한 소스파일은 이후로 "메인 소스파일"로 불린다.

4. Core Data 프레임워크를 프로젝트로 연결하고(Project > Add to Project 메뉴), 메인 소스파일로 해당 import 구문을 더한다(#import )

[편집] What happened?

당신은 아주 간단한 파운데이션 툴 프로젝트를 만들고 Core Data 프레임워크를 더했다. Core Data를 이용할 때 Application Kit은 필요없다. Xcode 데이타 모델링 툴도 역시 필수적인 것은 아니다. 다음 장에서 전체 모델을 코드로 작성할 것이다.그러나, 일반적으로 모델링 툴을 사용하는 것이 시간과 노력을 절약해 줄 것이다.

[편집] Creating the Managed Object Model

이 장에서 Run 엔티티를 설정하고 managed 오브젝트 모델을 만드는 법을 보일 것이다. Xcode를 통해 만드는 것이 가장 쉽지만, 이 튜토리얼에서는 전체 모델을 코드로 만들게 될 것이다.

Xcode에는 어플리케이션의 스키마를 정의하는데 일반적으로 사용하는 데이타 모델링 툴을 가지고 있다.( “Data Modeling Guide”의 ‘Creating a Managed Object Model Using Xcode’을 보라) Xcode데이타 모델링 툴은 복잡한 오브젝트의 집합들을 그래픽적으로 생성하게 하고, 런타임시에 언아카이브된다는 점에서 인터페이스 빌더와 유사하다. 인터페이스 빌더 없이 유저 인터페이스를 만드는 것은 가능하지만, 많은 노력을 요구한다. 유사하게, 합리적으로 직선적인 모델은 많은 코드를 필요로 하므로, 이 튜토리얼은 두개의 간단한 어트리뷰트를 가진 하나의 엔티티만을 사용한다.

[편집] Specifying the Entity

Run엔티티는 두개의 어트리뷰트를 가지고 있다. processID와 프로세스가 실행된 날짜이다. 둘 중 어떤 어트리뷰트도 옵션이 아니다 - 즉, 인스턴스가 정상적으로 판정되려면 값을 가져야만 한다( 만약 값 없이 인스턴스를 저장하려 한다면, 밸리데이션 에러를 얻을 것이다). processID는 디폴트 값으로 -1을 가진다. 밸리데이션 규칙과 결합하여, 이것은 값이 런타임시에 정상적으로 설정된다는 것을 말한다. 유틸리티에서 그 엔티티를 나타내는 클래스 또한 설정할 수 있다- 이 예제에서 "Run"이라는 이름의 커스텀 클래스를 사용할 것이다.

Name Type Optional Default Value Minimum Value
date date NO
processID int NO -1 0


표 3-1 Run 엔티티를 위한 어트리부트들

[편집] Create the Managed Object Model

Xcode에서 모델을 만들어서, 어플리케이션 서포트 디렉토리에 넣고, NSManagedObjectModel의 initWithContentsOfURL:을 이용해서 런타임시 로드할 수 있다. 그러나, 이 예제에서는 모델 전체의 생성을 코드로 한다.

[편집] Create the Model Instance

첫번째 단계는, 필요하다면 managed 오브젝트 모델 그 자체를 만드는 것이다.

1. 메인 소스파일의 맨 처음 main 앞에, 함수의 선언을 더한다

NSManagedObjectModel *managedObjectModel()

2. 메인 소스파일에서, managedObjectModel 함수를 수행한다. 이것은 managed 오브젝트 모델을 위한 정적 변수를 선언하고, 이것이 nil이 아니면 바로 리턴한다. nil이면, 새로운 managed 오브젝트 모델을 만들고 함수의 결과로 그것을 리턴한다.

NSManagedObjectModel *managedObjectModel() 
{ 
    static NSManagedObjectModel *mom = nil; 
    if (mom != nil) { return mom; } 
    mom = [[NSManagedObjectModel alloc] init]; 
    // implementation continues... 
    return mom; 
} 

리턴 구문 바로 뒤에( 코멘트 구문과 implementation continues..뒤) 다음 "Create the Entity"와 "Add the Attributes" 섹션에 묘사된 코드를 넣어야 할 것이다

[편집] Create the Entity

모델 그 자체를 만든 다음단계는, 엔티티를 만드는 것이다. 모델에 더하기 전에 엔티티 오브젝트의 이름을 설정해야 한다

1. 엔티티 디스크립션 오브젝트를 만들고, 그 이름과 managed 오브젝트 클래스 이름을 설정한다. 그리고 다음과 같이 모델에 더한다.

NSEntityDescription *runEntity = [[[NSEntityDescription alloc] init] autorelease]; 
[runEntity setName:@"Run"]; 
[runEntity setManagedObjectClassName:@"Run"]; 
[mom setEntities:[NSArray arrayWithObject:runEntity]];

[편집] Add the Attributes

어트리뷰트는 NSAttributeDescription의 인스턴스로 나타내어진다. 두개의 인스턴스를 만들어야 한다 - 하나는 날짜, 다른 하나는 processID - 그리고 그들의 특징을 적절하게 설정해야 한다.둘다 이름과 타입을 필요로 하며, 어떤 것도 옵션이 아니다. processID는 디폴트 값으로 -1을 가진다. processID 밸리데이션을 위해 프리디케이트도 만들어야 한다.

1. 다음과 같이 날짜 어트리뷰트 디스크립션을 만든다. 타입은 NSDateAttributeType이며 옵션은 아니다.

NSAttributeDescription *dateAttribute;
dateAttribute = [[[NSAttributeDescription alloc] init] autorelease]; 
[dateAttribute setName:@"date"]; 
[dateAttribute setAttributeType:NSDateAttributeType]; 
[dateAttribute setOptional:NO]; 

2. 다음과 같이 processID 어트리뷰트 디스크립션을 만든다. 타입은 NSInteger32AttributeType이며, 옵션이 아니다. 디폴트 값은 -1.

NSAttributeDescription *idAttribute; 
idAttribute = [[[NSAttributeDescription alloc] init] autorelease]; 
[idAttribute setName:@"processID"]; 
[idAttribute setAttributeType:NSInteger32AttributeType]; 
[idAttribute setOptional:NO]; 
[idAttribute setDefaultValue:[NSNumber numberWithInt:-1]];

3. processID를 위한 밸리데이션 프리디케이트를 만든다. 어트리뷰트 그 자체의 값은 zero보다 큰 값이어야 한다. 다음 코드는 validationPredicate = [NSPredicate predicateWithFormat:@"SELF >=0"]과 같지만, 이 예제는 long-hand 형식을 보여준다는 테마를 이어나간다.

NSPredicate *validationPredicate; 
NSExpression *lhs = [NSExpression expressionForEvaluatedObject]; 
NSExpression *rhs = [NSExpression expressionForConstantValue:[NSNumber 
numberWithInt:0]]; 
validationPredicate = [NSComparisonPredicate 
        predicateWithLeftExpression:lhs 
        rightExpression:rhs 
        modifier:NSDirectPredicateModifier 
        type:NSGreaterThanOrEqualToComparison 
        options:nil]; 

4. 각 밸리데이션 프리디케이트는 해당 에러 스트링을 요구한다. 전형적으로 에러 스트링은 적당하게 로컬라이즈 가능하다. 이 예제는 NSLocalizedString의 대강의 사용법을 보여주지만 추가적인 localization지원은 하지 않는다. 어트리뷰트 디스크립션을 프리디케이트의 어레이와 에러스트링의 어레이로 제공한다. 이 경우, 각각의 어레이는 하나의 오브젝트만을 포함한다.

NSString *validationWarning = NSLocalizedString 
        (@"Process ID must not be less than 0.", @"Process ID must not be less 
 than 0."); 
[idAttribute setValidationPredicates:[NSArray arrayWithObject:validationPredicate] 
        withValidationWarnings:[NSArray arrayWithObject:validationWarning]]; 

5. 마지막으로, 엔티티의 프라퍼티를 설정한다.

[runEntity setProperties: 
        [NSArray arrayWithObjects: dateAttribute, idAttribute, nil]];

[편집] Instantiate a Managed Object Model

지금까지 수행을 테스트 해 볼 수 있다. managed 오브젝트 모델을 인스턴트화 하고 모델의 디스크립션을 로그한다.

1. main 함수에서, 오토릴리즈 풀이 만들어진 후에, NSManagedObjectModel타입의 변수를 선언하고 그 값을 managedObjectModel함수 발생의 결과로 할당한다. NSLog를 이용해 모델 디스크립션을 프린트 한다.

NSManagedObjectModel *mom = managedObjectModel(); 
NSLog(@"The managed object model is defined as follows:\n%@", mom);
<./pre>

===Build and Test===

유틸리티를 빌드하고 실행시킨다. 경고 없이 컴파일 되어야 한다. 모델 파일의 로그된 디스크립션은 당신이 정의한 엔티티와 어트리뷰트를 포함 할 것이다. 이 장에서 모델은 아직 사용되지 않았으므로 그 isEditable 상태는 true임을 명심하라.

===Complete Listing===

managedObjectModel 함수의 완전한 리스팅은 다음과 같다.

<pre>
NSManagedObjectModel *managedObjectModel() 
{ 
    static NSManagedObjectModel *mom = nil; 
    if (mom != nil) { return mom; } 
    mom = [[NSManagedObjectModel alloc] init]; 
    NSEntityDescription *runEntity = [[NSEntityDescription alloc] init]; 
    [runEntity setName:@"Run"]; 
    [runEntity setManagedObjectClassName:@"Run"]; 
    [mom setEntities:[NSArray arrayWithObject:runEntity]]; 
    [runEntity release]; 
    NSAttributeDescription *dateAttribute; 
    dateAttribute = [[[NSAttributeDescription alloc] init] autorelease]; 
    [dateAttribute setName:@"date"]; 
    [dateAttribute setAttributeType:NSDateAttributeType]; 
    [dateAttribute setOptional:NO]; 
    NSAttributeDescription *idAttribute;
    idAttribute = [[[NSAttributeDescription alloc] init] autorelease]; 
    [idAttribute setName:@"processID"]; 
    [idAttribute setAttributeType:NSInteger32AttributeType]; 
    [idAttribute setOptional:NO]; 
    [idAttribute setDefaultValue:[NSNumber numberWithInt:0]]; 
    NSPredicate *validationPredicate = 
        [NSPredicate predicateWithFormat:@"SELF >= 0"]; 
    NSExpression *lhs = [NSExpression expressionForEvaluatedObject]; 
    NSExpression *rhs = [NSExpression expressionForConstantValue:[NSNumber 
numberWithInt:0]]; 
    validationPredicate = [NSComparisonPredicate 
        predicateWithLeftExpression:lhs 
        rightExpression:rhs 
        modifier:NSDirectPredicateModifier 
        type:NSGreaterThanOrEqualToComparison 
        options:nil]; 
    NSString *validationWarning = NSLocalizedString 
        (@"Process ID must not be less than 0.", "@Process ID must not be less 
 than 0."); 
    [idAttribute setValidationPredicates:[NSArray 
arrayWithObject:validationPredicate] 
        withValidationWarnings:[NSArray arrayWithObject:validationWarning]]; 
    [runEntity setProperties: 
            [NSArray arrayWithObjects: dateAttribute, idAttribute, nil]]; 
    return mom; 
} 


[편집] The Application Log Directory

유틸리티는 퍼시스턴트 스토어를 위한 파일을 저장할 어딘가가 필요하다. 이 섹션에서는 적당한 디렉토리를 정의하고 필요하다면 만드는 방법을 예시한다.이것은 이 유틸리티에는 유용한 개념이지만, Core Data에는 적절하지 않으므로 추가적인 설명은 주어지지 않는다.

[편집] The applicationLogDirectory Function

이 섹션에서는 퍼시스턴트 스토어를 위한 파일을 저장할 어딘가 적당한 디렉토리를 정의하고 필요하다면 만드는(~Library/Logs안에) 방법을 예시한다. 메인 소스파일에서, main이 함수를 정의하기 전에, NSString을 리턴하는 applicationLogDirectory()를 다음과 같이 수행한다.

NSString *applicationLogDirectory() 
{ 
    NSString *LOG_DIRECTORY = @"CDCLI"; 
    static NSString *ald = nil; 
    if (ald == nil) 
    { 
        NSArray *paths = NSSearchPathForDirectoriesInDomains 
                (NSLibraryDirectory, NSUserDomainMask, YES); 
        if ([paths count] == 1) 
        { 
            ald = [[paths objectAtIndex:0] 
stringByAppendingPathComponent:@"Logs"]; 
            ald = [[ald stringByAppendingPathComponent:LOG_DIRECTORY] retain]; 
            NSFileManager *fileManager = [NSFileManager defaultManager]; 
            BOOL isDirectory = NO; 
            if (![fileManager fileExistsAtPath:ald isDirectory:&isDirectory]) 
            { 
                if (![fileManager createDirectoryAtPath:ald attributes:nil]) 
                { 
                    [ald release]; 
                    ald = nil; 
                } 
            } 
            else 
            { 
                if (!isDirectory) 
                { 
                    [ald release]; 
                    ald = nil; 
                } 
            } 
        } 
    } 
    return ald; 
} 

[편집] Update the main Function

main함수에서, managedObjectModel함수의 발생 이후, applicationLogDirectory()를 발생시키고 그것이 nil을 리턴하지 않는지 확인한다. nil을 리턴하면, 에러를 보고하고 종료한다.

if (applicationLogDirectory() == nil) { 
    NSLog(@"Could not find application log directory\nExiting..."); 
    exit(1); 
} 

[편집] Build and Test

유틸리티를 빌드하고 실행시킨다. 경고 없이 컴파일 되어야 한다. 어플리케이션 로그 디렉토리는 정상적으로 만들어져야 하고, 에러가 로그되면 안된다.


[편집] Creating the Core Data Stack

이번 장에서는 managed오브젝트 로부터 하부에 깔린 퍼시스턴트 스토어로 Core Data 스택을 만들고 설정하는 법을 보여준다. 컨텍스트를 만드는 것은 쉽다 NSManagedObjectContext의 인스턴스를 얼로케이트하고 초기화 하기만 하면 된다. 보다 복잡한 부분은 설정을 상기하는 것이다. 퍼시스턴트 스토어 코디네이터를 만들고 설정해야 하며, 퍼시스턴트 스토어를 셋업해야 한다. managed 오브젝트 컨텍스트는 오브젝트 그래프를 관리하는데 책임이 있다. 퍼시스턴트 스토어를 관리하는 일은 퍼시스턴트 스토어 코디네이터로 떨어진다. 그 일은 managed 오브젝트 컨텍스트 혹은 콘텍스트들과 퍼시스턴트 스토어 혹은 스토어들 사이를 중재해 주는 것이다. 이것은 컨텍스트에 외관을 제공하고, 스토어의 컬렉션들을 하나의 가상 스토어로 나타내 준다. 이 예제에서, 코디네이터는 하나의 스토어를 관리한다. 스토어를 더하기 위해, NSPersistentStoreCoordinator의 addPersistentStoreWothType:configuration:URL:options:error: 메소드를 사용한다. 이것은 새로운 스토어를 나타내는 오브젝트를 리턴하거나, 만들어지지 않았을 경우 nil을 리턴한다( 현재 스토어 인스턴스를 조종하기 위한 퍼블릭 API는 없다. 그러나 그들은 NSPersistentStoreCoordinator의 다른 메소드들의 매개변수로서 사용될 수 있다). 파일 시스템에서 스토어의 위치뿐 아니라 스토어의 타입도 지정해 줘야 한다(이 예제는 모델 설정을 사용하지 않는다). 이 예제에서 사용되는 것은 XML스토어이다- 사람이 읽기에 좋으므로 테스트를 용이하게 한다. 파일이름의 확장자가 .xml이 아니라는 점을 기억하라. 원본 확장자를 사용하지 않을 수도 있다 - 모든 프로그램이 똑 같은 확장자를 사용한다고 상상해 보라

[편집] The managedObjectContext Function

managedObjectContext 함수의 주 목적은 적절하게 설정된 managed 오브젝트 컨텍스트를 리턴하는 것이다. 이 예제에서, 그렇게 하기 위해 Core Data 스택의 나머지 또한 설정해 줘야 한다.

[편집] Create the Context Instance

첫번째 단계는 필요하다면, managed 오브젝트 컨텍스트 인스턴스 그 자체를 만드는 것이다

1.메인소스파일의 맨 위에, main이전에 함수 선언을 더한다

NSManagedObjectContext *managedObjectContext().

2.메인 소스파일에서, managedObjectContext함수를 수행한다. 컨텍스트를 위한 정적변수를 선언한다. 변수가 nil이 아니면 즉시 리턴한다. nil이면, 새 컨텍스트를 만들고, 함수의 결과로 리턴한다.

NSManagedObjectContext *managedObjectContext() 
{ 
    static NSManagedObjectContext *moc = nil; 
    if (moc != nil) { return moc; } 
    moc = [[NSManagedObjectContext alloc] init]; 
    // implementation continues... 
    return moc; 
} 

[편집] Set up the Persistent Store Coordinator and Store

두번째 단계는 퍼시스턴트 스토어 코디네이터를 만들고 퍼시스턴트 스토어를 설정하는 것이다.

1.퍼시스턴트 스토어 코디네이터를 만들고, 컨텍스트를 위한 코디네이터를 설정한다.

NSPersistentStoreCoordinator *coordinator = 
        [[NSPersistentStoreCoordinator alloc] 
                initWithManagedObjectModel: managedObjectModel()]; 
[moc setPersistentStoreCoordinator: coordinator]; 
[coordinator release];

2. 적당한 타입의 퍼시스턴트 스토어를 만든다. 어떠한 이유로 스토어가 만들어지지 않는다면, 해당 경고를 로그한다.

NSString *STORE_TYPE = NSXMLStoreType; 
NSString *STORE_FILENAME = @"CDCLI.cdcli"; 
NSError *error; 
NSURL *url = [NSURL fileURLWithPath: [applicationLogDirectory() 
stringByAppendingPathComponent:STORE_FILENAME]]; 
id newStore = [coordinator addPersistentStoreWithType:STORE_TYPE 
                                        configuration:nil 
                                                  URL:url 
                                              options:nil 
                                                error:&error]; 
if (newStore == nil) { 
    NSLog(@"Store Configuration Failure\n%@", 
            ([error localizedDescription] != nil) ? 
            [error localizedDescription] : @"Unknown Error"); 
} 

[편집] Instantiate a Managed Object Context

그러므로 지금까지의 실행을 테스트 할 수 있었다. managed 오브젝트 컨텍스트를 초기화 하면서 1. main함수에서, managed 오부젝트 모델이 로그되었다는 설명 바로 다음 라인에, NSManagedObjectContext 타입의 변수를 선언하고 managedObjectContext 함수 실행 결과를 그 값으로 할당한다.

NSManagedObjectContext *moc = managedObjectContext();

[편집] Build and Test

유틸리티를 빌드하고 실행시킨다. 경고없이 컴파일 되어야 한다. 유틸리티를 실행시켰을 때, managedObjectContext 함수가 에러를 로그하지 않아야 한다.

[편집] Complete Listing

managedObjectContext함수의 전체 리스팅이다.

NSManagedObjectContext *managedObjectContext() 
{ 
    static NSManagedObjectContext *moc = nil; 
    if (moc != nil) return moc; 
    moc = [[NSManagedObjectContext alloc] init]; 
    NSPersistentStoreCoordinator *coordinator = 
        [[NSPersistentStoreCoordinator alloc] 
                initWithManagedObjectModel: managedObjectModel()]; 
    [moc setPersistentStoreCoordinator: coordinator]; 
    [coordinator release]; 
    NSString *STORE_TYPE = NSXMLStoreType; 
    NSString *STORE_FILENAME = @"CDCLI.cdcli"; 
    NSError *error; 
    NSURL *url = [NSURL fileURLWithPath: [applicationLogDirectory() 
stringByAppendingPathComponent:STORE_FILENAME]]; 
    id newStore = [coordinator addPersistentStoreWithType:STORE_TYPE 
                                            configuration:nil 
                                                      URL:url 
                                                  options:nil 
                                                    error:&error]; 
    if (newStore == nil) { 
        NSLog(@"Store Configuration Failure\n%@", 
                ([error localizedDescription] != nil) ? 
                [error localizedDescription] : @"Unknown Error"); 
    } 
    return moc; 
} 

[편집] The Custom Managed Object Calss

이 튜토리얼을 위한 managed 오브젝트 모델은 Run 엔티티가 커스텀 클래스 Run으로 나타나는 것으로 설정한다. 이 장에서는 스칼라 값을으로 어트리뷰트들 중 하나를 나타내는 클래스를 어떻게 수행할 것인지와 커스텀 엑세서 메소드를 정의 하는 법 그리고 새 인스턴스가 처음으로 생성되었을 때 발생하는 초기화기를 보인다. 전형적으로 예제를 위해 인스턴스 변수값을 더할 필요는 없다 - 이는 주로 Core Data 프레이웍 이 프라퍼티들을 관리하도록 두는 게 낫다- 그러나 이 예제에서는 processID어트리뷰트를 위해 스칼라값을 사용할 것이다. managed오브젝트에 프라퍼티값을 넣고 값을 얻기 위해 키-밸류 코딩을 사용할 수 있다. 그러나 커스텀 엑세서 메소드를 수행하는 것이 종종 더 편하다(컴파일-타임 타입의 점검을 할 수 있는 등). 스칼라 값을 이용해 나타내기 위해 선택한 모든 어트리뷰트들에 대해 커스텀 엑세서를 수행해야 한다. 스칼라 인스턴스 변수를 사용할 때의 한가지 약점은 nil 값을 표현 할 명백한 길이 없다는 것이다. NSKeyValueCoding 프로토콜은 setNilValueForKey:라는 스칼라 값을 nil로 설정하려고 시도할 때 지정할 수 있는 특별한 메소드를 정의한다. managed오브젝트를 초기화 하길 원하는 상황은 엄청나게 많다. 주어진 클래스의 인스턴스가 생길때 마다 초기화 수행을 원할 경우 그냥 간단히 지정 초기화기를 오버라이드 함으로서 수행 할 수 있다. 그 반면에, 오브젝트가 퍼시스턴트 스토어로부터 불러들여질때 혹은 보다 일반적으로, 오브젝트가 처음 생성될 때 각각 다른 초기화기를 수행하고 싶을 수 있다. Core Data는 각각의 상황에 특별한 메소드를 제공한다 - awakeFromFetch와 awakeFromInsert이다. 이 예제는 나중의 경우를 예시한다. 날짜와 시간을 새 레코드가 추가될 때 기록하고 그 값을 업데이트 하지 않는다.

[편집] Implementing the Managed Object Subclass

[편집] Create the Class Files

첫번째 단계는 새 클래스를 위해 파일을 만드는 것이다. 다른 어떤 클래스에도 원하는 만큼 쓸 수 있다,

1. Xcode에서, Run 클래스를 위한 새로운 Objective-C 클래스 파일(.h와 .m파일)을 더한다

2. Run.h파일에서, 클래스의 슈퍼클래스를 NSManagedObject로 정하고 date와 processID를 위한 엑세서 메소드를 정의한다. processID를 위해서는 int타입의 인스턴스 변수를 더한다.

@interface Run : NSManagedObject 
{ 
    int processID; 
} 
- (NSCalendarDate *)date; 
- (void)setDate:(NSCalendarDate *)newDate; 
- (int)processID; 
- (void)setProcessID:(int)newProcessID; 
@end

[편집] Implement the Accessor Methods

managed 오브젝트 클래스의 커스텀 엑세서를 수행할 때, 적당한 엑세스를 발생시켰는지와 통지 메소드를 변경시키는 것을 확인해야 한다.

1.날짜를 얻기 위한 엑세서를 수행한다. managed오브젝트의 프라이빗 인터널 저장공간으로부터 프리미티브 엑세서 메소드를 이용해서 값을 불러들인다.

<pre? - (NSCalendarDate *)date {

   [self willAccessValueForKey:@"date"]; 
   id date = [self primitiveValueForKey:@"date"]; 
   [self didAccessValueForKey:@"date"]; 
   return date; 

} </pre>

2.날짜를 넣기 위한 엑세서를 수행한다. managed오브젝트의 프라이빗 인터널 저장공간으로부터 프리미티브 엑세서 메소드를 이용해서 값을 넣는다. Core Data가 메모리 관리를 해 준다.

 
- (void)setDate:(NSCalendarDate *)newDate 
{ 
    [self willChangeValueForKey:@"date"]; 
    [self setPrimitiveValue:newDate forKey:@"date"]; 
    [self didChangeValueForKey:@"date"]; 
} 

3.processID를 얻기위한 엑세서를 수향한다. managed 오브젝트의 인스턴스 변수로부터 값을 불러들인다 - 그럼에도 불구하고 리시버가 fault일 경우를 대비해서 적당한 엑세스 통지 메소드를 발생시켜야 한다. 값은 퍼시스턴트 스토어로부터 불러들여진다.

- (int)processID 
{ 
    [self willAccessValueForKey:@"processID"]; 
    int pid = processID; 
    [self didAccessValueForKey:@"processID"]; 
    return pid; 
} 
</pre?

4.processID를 넣기 위한 엑세서를 수행한다. managed오브젝트의 인스턴스 변수 값으로 설정해 넣는다. 적당한 변경 통지 메소드 또한 호출해야 한다.

<pre>
- (void)setProcessID:(int)newProcessID 
{ 
    [self willChangeValueForKey:@"processID"]; 
    processID = newProcessID; 
    [self didChangeValueForKey:@"processID"]; 
} 
</pre?

===Dealing With nil Values===

스칼라 값을 이용해서 어트리뷰트를 나타낼 때, 키-밸류 코딩을 이용해서 값이 nil로 설정되는 일이 발생했을 경우를 대비할 필요가 있다. setNilValueForKey:메소드를 이용해 해결할 수 있다. 이 경우, 그냥 간단히 processID를 0으로 설정하는 것이다.

1.setNilValueForKey:메소드를 수행한다. 키가 "processID"이면 processID를 0으로 설정한다.

<pre>
- (void)setNilValueForKey:(NSString *)key 
{ 
    if ([key isEqualToString:@"processID"]) { 
        [self setProcessID:0]; 
    } 
    else { 
        [super setNilValueForKey:key]; 
    } 
} 


[편집] Implement the Initializer

NSManagedObject는 새 managed 오브젝트가 처음 만들어졌을때만 발생하고 연속적으로 퍼시스턴트 스토어로부터 패치될 때는 발생하지 않는 awakeFromInsert라는 특별한 메소드를 제공한다(엄격하게, managed 오브젝트 컨텍스트에 인서트 되었을 때). 여기에서 당신은 날짜와 시간을 새 레코드가 생성되었을 때 기록하고 나중에 업데이트 하지 않는다.

1.리시버의 날짜를 현재 날짜와 시간으로 설정하는 awakeFromInsert메소드를 수행한다

- (void) awakeFromInsert 
{ 
    [self setDate:[NSCalendarDate date]]; 
} 

[편집] Create an Instance of the Run Entity

NSEntityDescription은 주어진 엔티티의 새로운 인스턴스를 만들고 managed 오브젯트 컨텍스트에 넣는 편리한 메소드인 insertNewObjectForEntityForName:inManagedObjectContext:를 제공한다. 같은 효과를 직접 구현 할 수도 있다. 새 managed 오브젝트가 어느 엔티티의 인스턴스인지를 말할 수 있도록 managed 오브젝트 모델로부터 Run 엔티티 디스크립션을 불러들여야 한다. 새 인스턴스가 주어지면, 그 processID를 현재 프로세스의 ID로 설정할 수 있고, managed 오브젝트 컨텍스트에 퍼시스턴트 스토어의 변경을 승인하는 저장 메세지를 보낸다. 편이 메소드를 사용하는 이점은 더 편하다는 것이다. 특히 커스텀 managed 오브젝트 클래스의 이름으로 변경에 대한 복귀를 할 수도 있다.

1.main소스파일에서, Run 클래스를 임포트 한다.

#import "Run.h"

2.메인 함수에서, managedObjectContext()함수의 발생 뒤에, Run 클래스의 새 인스턴스를 만든다.

NSEntityDescription *runEntity = [[mom entitiesByName] objectForKey:@"Run"]; 
Run *run = [[[Run alloc] initWithEntity:runEntity 
                insertIntoManagedObjectContext:moc] autorelease]; 

3.현재 프로세스의 processID를 얻고, Run 오브젝트의 processID를 설정한다.

NSProcessInfo *processInfo = [NSProcessInfo processInfo]; 
[run setProcessID:[processInfo processIdentifier]];

4.managed 오브젝트 컨텍스트를 저장함으로서 퍼시스턴트 스토어에 대한 변경을 승인한다. 에러를 점검하고, 에러가 발생했으면 종료한다.

NSError *error = nil; 
if (![moc save: &error]) { 
    NSLog(@"Error while saving\n%@", 
        ([error localizedDescription] != nil) ? [error localizedDescription] : 
 @"Unknown Error"); 
    exit(1); 
} 


[편집] Build and Test

유틸리티를 빌드하고 실행한다. 경고 없이 컴파일 되어야 한다. 유틸리티를 실행시켰을 때, 에러를 로그하지 않아야 한다. 어플리케이션 로그 디렉토리에 새 파일이 만들어지는 것을 볼 수 있어야 한다. 그 파일을 검사했을 때, 실행 오브젝트들에 대한 세부사항을 포함하고 있는 것을 확인할 수 있어야 한다. 다른 사항들을 테스트 해 보라. Run 오브젝트의 processID를 설정하는 라인을 코멘트 해 본다. 유틸리티를 빌드하고 실행한다. 무슨일이 생겼는가( processID의 디폴트 값으로 -1을 리콜한다). processID를 nil로 설정하기 위해 키-밸류 코딩을 사용한다. 유틸리티를 다시 빌드하고 실행시킨다. 무슨일이 일어났나? 그리고 마지막으로, setNilValueForKey:메소드를 코멘트 하고 다시한번 테스트 해 본다.

[편집] Complete Listings

[편집] The Run Class

Run클래스의 선언과 수행의 완전한 리스팅은 다음과 같다

@interface Run : NSManagedObject
{ 
    int processID; 
} 
- (NSCalendarDate *)date; 
- (void)setDate:(NSCalendarDate *)newDate; 
- (int)processID; 
- (void)setProcessID:(int)newProcessID; 
@end 
@implementation Run 
- (void) awakeFromInsert 
{ 
    [self setDate:[NSCalendarDate date]]; 
} 
- (NSCalendarDate *)date 
{ 
    [self willAccessValueForKey:@"date"]; 
    id o = [self primitiveValueForKey:@"date"]; 
    [self didAccessValueForKey:@"date"]; 
    return o; 
} 
- (void)setDate:(NSCalendarDate *)newDate 
{ 
    [self willChangeValueForKey:@"date"]; 
    [self setPrimitiveValue:newDate forKey:@"date"]; 
    [self didChangeValueForKey:@"date"]; 
} 
- (int)processID 
{ 
    [self willAccessValueForKey:@"processID"]; 
    int pid = processID; 
    [self didAccessValueForKey:@"processID"]; 
    return pid; 
} 
- (void)setProcessID:(int)newProcessID 
{ 
    [self willChangeValueForKey:@"processID"]; 
    processID = newProcessID; 
    [self didChangeValueForKey:@"processID"]; 
} 
- (void)setNilValueForKey:(NSString *)key 
{ 
    if ([key isEqualToString:@"processID"]) { 
        [self setProcessID:0]; 
    } 
    else { 
        [super setNilValueForKey:key]; 
    } 
} 
@end 

[편집] The main() Function

main함수는 다음과 같다

int main (int argc, const char * argv[]) { 
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 
    NSManagedObjectModel *mom = managedObjectModel(); 
    NSLog(@"mom: %@", mom); 
    if (applicationLogDirectory() == nil) { 
        NSLog(@"Could not find application logs directory\nExiting..."); 
        exit(1); 
    } 
    NSManagedObjectContext *moc = managedObjectContext(); 
    NSEntityDescription *runEntity = [[mom entitiesByName] objectForKey:@"Run"]; 
    Run *run = [[[Run alloc] initWithEntity:runEntity 
            insertIntoManagedObjectContext:moc] autorelease]; 
    NSProcessInfo *processInfo = [NSProcessInfo processInfo]; 
    [run setProcessID:[processInfo processIdentifier]]; 
    NSError *error = nil; 
    if (![moc save: &error]) { 
        NSLog(@"Error while saving\n%@", 
            ([error localizedDescription] != nil) ? [error localizedDescription] 
 : @"Unknown Error"); 
        exit(1); 
    } 
    // Implementation will continue... 
    [pool release]; 
    return 0; 
} 

[편집] Listing Previous Runs

이 섹션에서는 퍼시스턴트 스토어로부터 모든 Run 인스턴스를 패치하는 법을 보여준다

[편집] Fetching Run Objects

[편집] Create and Issue the Fetch Request

첫번째 단계는 패치 리쿼스트를 만드는 것이다. Run 엔티티의 인스턴스를 패치하길 원하고 그 결과를 최근 순서로 정렬하려 한다. Run 엔티티를 위한 패치 리퀘스트의 엔티티를 설정하고 적당한 정렬 순서를 위한 어레이를 만들고 설정한다. 마지막으로, managed 오브젝트 컨텍스트에 executeFetchRequest:request error:메세지를 보내어 패치를 실행한다.

1. main함수에서, 이전 장에서 추가한 코드 바로 뒤에, 새로운 패치 리퀘스트를 만들고 엔티티를 설정한다(Run의 새 인스턴스를 만들기 위해 이전 장에서 Run 엔티티 디스크립션을 다실 불러온다)

NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease]; 
[request setEntity:runEntity]; 


2. 패치 결과를 최근 순서로 정렬하기 위한 소스 디스크립터를 만든다. 그 소트 디스크립터를 패치를 위해 설정한다- 소트 디스크립터의 어레이를 공급해야 한다는 것을 명심하라.

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] 
        initWithKey:@"date" ascending:YES]; 
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];

3.managed 오브젝트 컨텍스트로 보냄으로서 패치 리퀘스트를 발행한다. 이전 장에서 에러를 정의했다는 것을 기억하라. 에러가 있다면, 보고하고 종료한다.

error = nil; 
NSArray *array = [moc executeFetchRequest:request error:&error]; 
if ((error != nil) || (array == nil)) { 
    NSLog(@"Error while fetching\n%@", 
            ([error localizedDescription] != nil) ? [error localizedDescription] 
 : @"Unknown Error"); 
    exit(1); 
} 

[편집] Display the Results

패치된 런 오브젝트에 되풀이하고 런 인포메이션을 로그한다.

1. 시간 정보를 표시하기 위한 날짜 포매터를 만든다
NSString *dateFormatString = [[NSUserDefaults standardUserDefaults] 
        stringForKey:NSShortTimeDateFormatString]; 
NSDateFormatter *formatter = [[NSDateFormatter alloc] 
        initWithDateFormat:dateFormatString allowNaturalLanguage:NO]; 

2. 프로세스에 대한 런 히스토리를 프린트 하고, 포매터를 해제한다.

NSLog(@"%@ run history:", [processInfo processName]); 
NSEnumerator *runEnumerator = [array objectEnumerator]; 
while (run = [runEnumerator nextObject]) { 
    NSLog(@"On %@ as process ID %d", 
            [formatter stringForObjectValue:[run date]], 
            [run processID]); 
} 
[formatter release];

[편집] Build and Test

유틸리티를 빌드하고 실행한다. 경고 없이 컴파일 되어야 한다. 유틸리티를 실행시켰을 때, 에러를 로그하지 않아야 한다. 적당한 런 히스토리를 표시해 주어야 한다.

[편집] Complete Listing

main 소스파일에 대한 전체 listing은 다음과 같다.

#import <Foundation/Foundation.h> 
#import <CoreData/CoreData.h> 
#import "Run.h" 
NSManagedObjectModel *managedObjectModel(); 
NSString *applicationSupportDirectory(); 
NSManagedObjectContext *managedObjectContext(); 
int main (int argc, const char * argv[]) { 
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 
    NSManagedObjectModel *mom = managedObjectModel(); 
    NSLog(@"mom: %@", mom); 
    if (applicationSupportDirectory() == nil) { 
        NSLog(@"Could not find application support directory\nExiting..."); 
        exit(1); 
    } 
    NSManagedObjectContext *moc = managedObjectContext(); 
    NSEntityDescription *runEntity = [[mom entitiesByName] objectForKey:@"Run"]; 
    Run *run = [[[Run alloc] initWithEntity:runEntity 
            insertIntoManagedObjectContext:moc] autorelease]; 
    NSProcessInfo *processInfo = [NSProcessInfo processInfo]; 
    [run setProcessID:[processInfo processIdentifier]]; 
    NSError *error = nil; 
    if (![managedObjectContext() save: &error]) { 
        NSLog(@"Error while saving\n%@", 
            ([error localizedDescription] != nil) ? [error localizedDescription] 
 : @"Unknown Error"); 
        exit(1); 
    } 
    NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease]; 
    [request setEntity:runEntity]; 
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] 
            initWithKey:@"date" ascending:YES]; 
    [request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]]; 
    error = nil; 
    NSArray *array = [moc executeFetchRequest:request error:&error]; 
    if ((error != nil) || (array == nil)) { 
        NSLog(@"Error while fetching\n%@", 
                ([error localizedDescription] != nil) ? [error 
localizedDescription] : @"Unknown Error"); 
        exit(1); 
    } 
    NSString *dateFormatString = [[NSUserDefaults standardUserDefaults] 
        stringForKey:NSShortTimeDateFormatString]; 
    NSDateFormatter *formatter = [[NSDateFormatter alloc] 
        initWithDateFormat:dateFormatString allowNaturalLanguage:NO]; 
    NSLog(@"%@ run history:", [processInfo processName]); 
    NSEnumerator *runEnumerator = [array objectEnumerator]; 
    while (run = [runEnumerator nextObject]) { 
        NSLog(@"On %@ as process ID %d", 
                [formatter stringForObjectValue:[run date]], 
                [run processID]); 
    } 
    [formatter release];
    [pool release]; 
    return 0; 
} 
NSManagedObjectModel *managedObjectModel() 
{ 
    static NSManagedObjectModel *mom = nil; 
    if (mom != nil) { return mom; } 
    mom = [[NSManagedObjectModel alloc] init]; 
    NSEntityDescription *runEntity = [[NSEntityDescription alloc] init]; 
    [runEntity setName:@"Run"]; 
    [runEntity setManagedObjectClassName:@"Run"]; 
    [mom setEntities:[NSArray arrayWithObject:runEntity]]; 
    [runEntity release]; 
    NSAttributeDescription *dateAttribute; 
    dateAttribute = [[[NSAttributeDescription alloc] init] autorelease]; 
    [dateAttribute setName:@"date"]; 
    [dateAttribute setAttributeType:NSDateAttributeType]; 
    [dateAttribute setOptional:NO]; 
    NSAttributeDescription *idAttribute; 
    idAttribute = [[[NSAttributeDescription alloc] init] autorelease]; 
    [idAttribute setName:@"processID"]; 
    [idAttribute setAttributeType:NSInteger32AttributeType]; 
    [idAttribute setOptional:NO]; 
    [idAttribute setDefaultValue:[NSNumber numberWithInt:0]]; 
    NSPredicate *validationPredicate = 
        [NSPredicate predicateWithFormat:@"SELF >= 0"]; 
    NSExpression *lhs = [NSExpression expressionForEvaluatedObject]; 
    NSExpression *rhs = [NSExpression expressionForConstantValue:[NSNumber 
numberWithInt:0]]; 
    validationPredicate = [NSComparisonPredicate 
        predicateWithLeftExpression:lhs 
        rightExpression:rhs 
        modifier:NSDirectPredicateModifier 
        type:NSGreaterThanOrEqualToComparison 
        options:nil]; 
    NSString *validationWarning = NSLocalizedString 
        (@"Process ID must not be less than 0.", "@Process ID must not be less 
 than 0."); 
    [idAttribute setValidationPredicates:[NSArray 
arrayWithObject:validationPredicate] 
        withValidationWarnings:[NSArray arrayWithObject:validationWarning]]; 
    [runEntity setProperties: 
            [NSArray arrayWithObjects: dateAttribute, idAttribute, nil]]; 
    return mom; 
} 

NSString *applicationSupportDirectory() 
{ 
    NSString *SUPPORT_DIRECTORY = @"CDCLI"; 
    static NSString *asd = nil; 
    if (asd == nil) { 
        asd = [[[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Logs"] 
                stringByAppendingPathComponent:SUPPORT_DIRECTORY] retain]; 
        NSFileManager *fileManager = [NSFileManager defaultManager]; 
        BOOL isDirectory = NO; 
        if ([fileManager fileExistsAtPath:asd isDirectory:&isDirectory]) { 
            if (!isDirectory) { 
                [asd release]; 
                asd = nil; 
            } 
        } 
        else { 
            if (![fileManager createDirectoryAtPath:asd attributes:nil]) { 
                [asd release]; 
                asd = nil; 
            } 
        } 
    } 
    return asd; 
} 
NSManagedObjectContext *managedObjectContext() 
{ 
    static NSManagedObjectContext *moc = nil; 
    if (moc != nil) return moc; 
    moc = [[NSManagedObjectContext alloc] init]; 
    NSPersistentStoreCoordinator *coordinator = 
        [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: 
managedObjectModel()]; 
    [moc setPersistentStoreCoordinator: coordinator]; 
    NSString *STORE_TYPE = NSXMLStoreType; 
    NSString *STORE_FILENAME = @"CDCLI.cdcli"; 
    NSError *error; 
    NSURL *url = [NSURL fileURLWithPath: [applicationSupportDirectory() 
stringByAppendingPathComponent:STORE_FILENAME]]; 
    id newStore = [coordinator addPersistentStoreWithType:STORE_TYPE 
                                            configuration:nil 
                                                      URL:url 
                                                  options:nil 
                                                    error:&error]; 
    if (newStore == nil) { 
        NSLog(@"Store Configuration Failure\n%@", 
                ([error localizedDescription] != nil) ? 
                [error localizedDescription] : @"Unknown Error"); 
    } 
    return moc; 
}