내 iPhone 앱은 핵심 데이터 저장소를 마이그레이션해야하며 일부 데이터베이스는 상당히 큽니다. Apple의 문서에서는 "다중 패스"를 사용하여 데이터를 마이그레이션하여 메모리 사용을 줄일 것을 제안합니다. 그러나 문서는 매우 제한적이며 실제로이를 수행하는 방법을 잘 설명하지 않습니다. 누군가가 나를 좋은 예로 안내하거나 실제로이 작업을 수행하는 방법을 자세히 설명 할 수 있습니까?
내 iPhone 앱은 핵심 데이터 저장소를 마이그레이션해야하며 일부 데이터베이스는 상당히 큽니다. Apple의 문서에서는 "다중 패스"를 사용하여 데이터를 마이그레이션하여 메모리 사용을 줄일 것을 제안합니다. 그러나 문서는 매우 제한적이며 실제로이를 수행하는 방법을 잘 설명하지 않습니다. 누군가가 나를 좋은 예로 안내하거나 실제로이 작업을 수행하는 방법을 자세히 설명 할 수 있습니까?
답변:
나는 그들의 문서 에서 Apple이 암시하는 바를 알아 냈다 . 실제로는 매우 쉽지만 분명하기까지는 갈 길이 멀다. 예를 들어 설명을 설명하겠습니다. 초기 상황은 다음과 같습니다.
"핵심 데이터 저장소가있는 탐색 기반 앱"템플릿을 사용하여 프로젝트를 만들 때 얻는 모델입니다. 나는 그것을 컴파일하고 약간 다른 값을 가진 약 2k 항목을 만들기 위해 for 루프의 도움으로 약간의 타격을가했습니다. NSDate 값을 가진 2.000 개의 이벤트가 있습니다.
이제 다음과 같은 두 번째 버전의 데이터 모델을 추가합니다.
차이점은 이벤트 엔티티가 사라지고 두 개의 새 엔티티가 있다는 것입니다. 타임 스탬프를로 저장하는 double
하나와 날짜를 NSString
.
목표는 모든 버전 1 이벤트를 두 개의 새 엔티티 로 전송 하고 마이그레이션과 함께 값을 변환하는 것입니다. 이로 인해 별도의 엔터티에서 다른 유형으로 각각 두 배의 값이 생성됩니다.
마이그레이션을 위해 우리는 수동 마이그레이션을 선택하고 매핑 모델을 사용합니다. 이것은 또한 귀하의 질문에 대한 답변의 첫 번째 부분입니다. 2k 항목을 마이그레이션하는 데 시간이 오래 걸리고 메모리 풋 프린트를 낮게 유지하기를 원하기 때문에 두 단계로 마이그레이션을 수행 할 것입니다.
계속해서 이러한 매핑 모델을 더 분할하여 엔티티 범위 만 마이그레이션 할 수도 있습니다. 백만 개의 레코드가 있다고 가정하면 전체 프로세스가 중단 될 수 있습니다. Filter predicate를 사용하여 가져온 항목의 범위를 좁힐 수 있습니다 .
다음과 같이 첫 번째 매핑 모델을 만듭니다.
1. 새 파일-> 리소스-> 매핑 모델
2. 이름을 선택하고 StepOne을 선택했습니다.
3. 소스 및 대상 데이터 모델 설정
멀티 패스 마이그레이션에는 사용자 지정 엔터티 마이그레이션 정책이 필요하지 않지만이 예제에 대해 좀 더 자세히 알아보기 위해 수행합니다. 따라서 엔터티에 사용자 지정 정책을 추가합니다. 이것은 항상의 하위 클래스입니다 NSEntityMigrationPolicy
.
이 정책 클래스는 마이그레이션을 수행하는 몇 가지 방법을 구현합니다. 그러나이 경우에는 간단하므로 하나의 메소드 만 구현해야합니다 createDestinationInstancesForSourceInstance:entityMapping:manager:error:
..
구현은 다음과 같습니다.
#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 : 아니]; 또한 오래된 데이터 타이머를 낮게 설정하여 메모리가 자주 플러시되도록하십시오.