这些问题是相关的:
内存问题在iPhone上迁移大型CoreData数据存储区
在使用iOS的块中进行多次传递核心数据迁移
引用第一个链接:
这在官方讨论 “多次通过”中的文档 部分,但它看起来像他们的 建议的方法是分开 您按实体类型迁移,即 制作多个映射模型 迁移实体的子集 完整数据模型中的类型。
我已经弄明白了苹果在他们身上的暗示 文件 。它实际上非常简单,但在它显而易见之前还有很长的路要走。我将用一个例子说明解释。最初的情况是这样的:
这是使用“基于导航的应用程序和核心数据存储”模板创建项目时获得的模型。我编译了它,并在for循环的帮助下做了一些努力,创建了大约2k个条目,所有条目都有一些不同的值。我们用NSDate值去了2000个事件。
现在我们添加第二个版本的数据模型,如下所示:
区别在于:Event实体已经消失,我们有两个新的实体。一个将时间戳存储为 double 第二个应该存储日期的 NSString 。
double
NSString
目标是转移所有 的 版本1 强> 两个新实体的事件,并在迁移过程中转换值。这导致两个值作为单独的权利中的不同类型的两倍。
要进行迁移,我们选择手动迁移,我们使用映射模型。这也是你问题答案的第一部分。我们将分两步进行迁移,因为迁移2k条目需要很长时间,我们希望保持较低的内存占用。
您甚至可以继续拆分这些映射模型,以仅迁移实体的范围。假设我们有一百万条记录,这可能会导致整个过程崩溃。可以使用a缩小获取的实体的范围 过滤谓词 。
我们创建第一个这样的映射模型:
1.新文件 - >资源 - >映射模型
2.选择一个名字,我选择了StepOne
3.设置源和目标数据模型
多次传递迁移不需要自定义实体迁移策略,但是我们将这样做以获得此示例的更多详细信息。因此,我们向实体添加自定义策略。这始终是子类 NSEntityMigrationPolicy 。
NSEntityMigrationPolicy
此策略类实现了一些方法来实现迁移。但是在这种情况下它很简单,所以我们只需要实现一个方法: createDestinationInstancesForSourceInstance:entityMapping:manager:error: 。
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。
最后,我们需要触发迁移。我暂时跳过样板代码。如果您需要,我会在这里发布。它可以在 <一个href =“https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/vmCustomizing.html#//apple_ref/doc/uid/TP40004399-CH8-SW1"rel =”noreferrer“ > 自定义迁移过程 它只是前两个代码示例的合并。第三部分和最后一部分将被修改如下:而不是使用类的方法 NSMappingModel 类 mappingModelFromBundles:forSourceModel:destinationModel: 我们会用的 initWithContentsOfURL: 因为类方法只返回一个,也许是第一个,在bundle中找到的映射模型。
NSMappingModel
mappingModelFromBundles:forSourceModel:destinationModel:
initWithContentsOfURL:
现在我们有了两个映射模型,可以在lopp的每次传递中使用,并将migrate方法发送到迁移管理器。而已。
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 在捆绑中。
cdm
必须提供目标存储,不应该是源存储。成功迁移后,您可以删除旧版本并重命名新版本。
在创建映射模型后,我对数据模型进行了一些更改,这导致了一些兼容性错误,我只能通过重新创建映射模型来解决这些问题。
假设您的数据库架构有5个实体,例如使用标准种类的例子的人,学生,课程,课程和注册,其中学生亚类人,班级实施课程,注册加入班级和学生。如果您对所有这些表定义进行了更改,则必须从基类开始,然后逐步完成。所以,你不能从转换注册开始,因为每个注册记录都取决于那里的班级和学生。因此,您将首先仅迁移Person表,将现有行复制到新表中,并填写任何新字段(如果可能)并丢弃已删除的列。在自动释放池中进行每次迁移,以便一旦完成,您的内存就会重新开始。
Person表完成后,您可以转换学生表。然后跳到Course然后是Class,最后是Register表。
另一个考虑因素是记录的数量,如果Person有一千行,你必须每100个左右执行NSManagedObject等同于一个版本,这是告诉托管对象的上下文[moc refreshObject:ob mergeChanges:没有]; 还要将过时的数据计时器设置为低,以便经常刷新内存。