[iphone] 다중 패스를 사용한 핵심 데이터 마이그레이션의 예 또는 설명?

내 iPhone 앱은 핵심 데이터 저장소를 마이그레이션해야하며 일부 데이터베이스는 상당히 큽니다. Apple의 문서에서는 “다중 패스”를 사용하여 데이터를 마이그레이션하여 메모리 사용을 줄일 것을 제안합니다. 그러나 문서는 매우 제한적이며 실제로이를 수행하는 방법을 잘 설명하지 않습니다. 누군가가 나를 좋은 예로 안내하거나 실제로이 작업을 수행하는 방법을 자세히 설명 할 수 있습니까?



답변

나는 그들의 문서 에서 Apple이 암시하는 바를 알아 냈다 . 실제로는 매우 쉽지만 분명하기까지는 갈 길이 멀다. 예를 들어 설명을 설명하겠습니다. 초기 상황은 다음과 같습니다.

데이터 모델 버전 1

여기에 이미지 설명 입력
여기에 이미지 설명 입력

“핵심 데이터 저장소가있는 탐색 기반 앱”템플릿을 사용하여 프로젝트를 만들 때 얻는 모델입니다. 나는 그것을 컴파일하고 약간 다른 값을 가진 약 2k 항목을 만들기 위해 for 루프의 도움으로 약간의 타격을가했습니다. NSDate 값을 가진 2.000 개의 이벤트가 있습니다.

이제 다음과 같은 두 번째 버전의 데이터 모델을 추가합니다.

여기에 이미지 설명 입력

데이터 모델 버전 2

차이점은 이벤트 엔티티가 사라지고 두 개의 새 엔티티가 있다는 것입니다. 타임 스탬프를로 저장하는 double하나와 날짜를 NSString.

목표는 모든 버전 1 이벤트를 두 개의 새 엔티티 로 전송 하고 마이그레이션과 함께 값을 변환하는 것입니다. 이로 인해 별도의 엔터티에서 다른 유형으로 각각 두 배의 값이 생성됩니다.

마이그레이션을 위해 우리는 수동 마이그레이션을 선택하고 매핑 모델을 사용합니다. 이것은 또한 귀하의 질문에 대한 답변의 첫 번째 부분입니다. 2k 항목을 마이그레이션하는 데 시간이 오래 걸리고 메모리 풋 프린트를 낮게 유지하기를 원하기 때문에 두 단계로 마이그레이션을 수행 할 것입니다.

계속해서 이러한 매핑 모델을 더 분할하여 엔티티 범위 만 마이그레이션 할 수도 있습니다. 백만 개의 레코드가 있다고 가정하면 전체 프로세스가 중단 될 수 있습니다. Filter predicate를 사용하여 가져온 항목의 범위를 좁힐 수 있습니다 .

두 가지 매핑 모델로 돌아갑니다.

다음과 같이 첫 번째 매핑 모델을 만듭니다.

1. 새 파일-> 리소스-> 매핑 모델
여기에 이미지 설명 입력

2. 이름을 선택하고 StepOne을 선택했습니다.

3. 소스 및 대상 데이터 모델 설정

여기에 이미지 설명 입력

모델 매핑 1 단계

여기에 이미지 설명 입력

여기에 이미지 설명 입력

여기에 이미지 설명 입력

멀티 패스 마이그레이션에는 사용자 지정 엔터티 마이그레이션 정책이 필요하지 않지만이 예제에 대해 좀 더 자세히 알아보기 위해 수행합니다. 따라서 엔터티에 사용자 지정 정책을 추가합니다. 이것은 항상의 하위 클래스입니다 NSEntityMigrationPolicy.

여기에 이미지 설명 입력

이 정책 클래스는 마이그레이션을 수행하는 몇 가지 방법을 구현합니다. 그러나이 경우에는 간단하므로 하나의 메소드 만 구현해야합니다 createDestinationInstancesForSourceInstance:entityMapping:manager:error:..

구현은 다음과 같습니다.

StepOneEntityMigrationPolicy.m

#import "StepOneEntityMigrationPolicy.h"


@implementation StepOneEntityMigrationPolicy

- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)sInstance
                                      entityMapping:(NSEntityMapping *)mapping
                                            manager:(NSMigrationManager *)manager
                                              error:(NSError **)error
{
    // Create a new object for the model context
    NSManagedObject *newObject =
        [NSEntityDescription insertNewObjectForEntityForName:[mapping destinationEntityName]
                                      inManagedObjectContext:[manager destinationContext]];

    // do our transfer of nsdate to nsstring
    NSDate *date = [sInstance valueForKey:@"timeStamp"];
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setTimeStyle:NSDateFormatterMediumStyle];
    [dateFormatter setDateStyle:NSDateFormatterMediumStyle];

    // set the value for our new object
    [newObject setValue:[dateFormatter stringFromDate:date] forKey:@"printedDate"];
    [dateFormatter release];

    // do the coupling of old and new
    [manager associateSourceInstance:sInstance withDestinationInstance:newObject forEntityMapping:mapping];

    return YES;
}

마지막 단계 : 마이그레이션 자체

NSDate를 double로 변환하는 데 사용되는 timeIntervalSince1970 만 거의 동일한 두 번째 매핑 모델을 설정하는 부분은 건너 뛰겠습니다.

마지막으로 마이그레이션을 트리거해야합니다. 지금은 상용구 코드를 건너 뛰겠습니다. 필요한 경우 여기에 게시하겠습니다. 마이그레이션 프로세스 사용자 지정에서 찾을 수 있으며 처음 두 코드 예제를 병합 한 것입니다. 다음과 같이 세 번째와 마지막 부분은 수정됩니다 : 대신의 클래스 메소드 사용하는 NSMappingModel클래스를 mappingModelFromBundles:forSourceModel:destinationModel:우리가 사용됩니다 initWithContentsOfURL:클래스 방법은 단 하나, 번들 어쩌면 처음 발견 매핑 모델을 반환하기 때문이다.

이제 루프의 모든 단계에서 사용할 수있는 두 가지 매핑 모델이 있으며 마이그레이션 메서드를 마이그레이션 관리자에게 보냅니다. 그게 다야.

NSArray *mappingModelNames = [NSArray arrayWithObjects:@"StepOne", @"StepTwo", nil];
NSDictionary *sourceStoreOptions = nil;

NSURL *destinationStoreURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreDataMigrationNew.sqlite"];

NSString *destinationStoreType = NSSQLiteStoreType;

NSDictionary *destinationStoreOptions = nil;

for (NSString *mappingModelName in mappingModelNames) {
    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:mappingModelName withExtension:@"cdm"];

    NSMappingModel *mappingModel = [[NSMappingModel alloc] initWithContentsOfURL:fileURL];

    BOOL ok = [migrationManager migrateStoreFromURL:sourceStoreURL
                                               type:sourceStoreType
                                            options:sourceStoreOptions
                                   withMappingModel:mappingModel
                                   toDestinationURL:destinationStoreURL
                                    destinationType:destinationStoreType
                                 destinationOptions:destinationStoreOptions
                                              error:&error2];
    [mappingModel release];
}

메모

  • 매핑 모델 cdm은 번들로 끝납니다 .

  • 대상 저장소를 제공해야하며 원본 저장소가 아니어야합니다. 마이그레이션에 성공한 후 이전 항목을 삭제하고 새 이름을 바꿀 수 있습니다.

  • 매핑 모델을 만든 후 데이터 모델을 약간 변경했는데 이로 인해 호환성 오류가 발생하여 매핑 모델을 다시 만들어야 만 해결할 수있었습니다.


답변

다음 질문은 관련이 있습니다.

iPhone에서 대용량 CoreData 데이터 저장소를 마이그레이션하는 메모리 문제

iOS를 사용한 청크의 다중 패스 코어 데이터 마이그레이션

첫 번째 링크를 인용하려면 :

이것은 “Multiple Passes”섹션의 공식 문서에서 논의되지만 제안 된 접근 방식은 엔티티 유형별로 마이그레이션을 나누는 것입니다. 즉, 여러 매핑 모델을 만들고, 각각은 완전한 데이터 모델.


답변

데이터베이스 스키마에 person, student, course, class, registration 등 5 개의 엔티티가 있다고 가정 해 보겠습니다. 여기에서 student는 person을 하위 클래스로, 클래스는 코스를 구현하며 등록은 class와 student에 합류합니다. 이러한 모든 테이블 정의를 변경 한 경우 기본 클래스에서 시작하여 작업을 진행해야합니다. 따라서 각 등록 기록은 수업과 학생이 거기에 있는지에 따라 다르기 때문에 등록 변환으로 시작할 수 없습니다. 따라서 Person 테이블 만 마이그레이션하고 기존 행을 새 테이블에 복사하고 가능한 경우 새 필드를 채우고 제거 된 열을 버리는 것으로 시작합니다. 자동 릴리스 풀 내에서 각 마이그레이션을 수행하여 완료되면 메모리가 다시 시작되도록합니다.

Person 테이블이 완료되면 학생 테이블을 다시 변환 할 수 있습니다. 그런 다음 Course, Class, 마지막으로 Registration 테이블로 이동합니다.

다른 고려 사항은 레코드의 수입니다. Person처럼 행이 천 개이면 100 개 정도마다 릴리스에 해당하는 NSManagedObject를 실행해야합니다. 이는 관리되는 개체 컨텍스트에 알리는 것입니다 [moc refreshObject : ob mergeChanges : 아니]; 또한 오래된 데이터 타이머를 낮게 설정하여 메모리가 자주 플러시되도록하십시오.


답변