核心数据迁移示例或说明多个通行证?

我的iPhone应用程序需要迁移其核心数据存储,而一些数据库相当大。 苹果的文档build议使用“多次传递”来迁移数据以减less内存使用。 然而,文件是非常有限的,并不能很好地解释如何实际做到这一点。 有人能指出我是一个很好的例子,还是详细解释如何真正把这个过程去掉?

我已经知道苹果在文档中提示什么了。 这其实很简单,但在很明显之前还有很长的路要走。 我将以一个例子来说明解释。 最初的情况是这样的:

数据模型版本1

在这里输入图像描述在这里输入图像描述

这是您使用“基于导航的应用程序和核心数据存储”模板创build项目时获得的模型。 我编译它,并用for循环创build了大约2k个条目,并使用一些不同的值创build了一些很难的东西。 在那里我们去2.000事件与一个NSDate值。

现在我们添加数据模型的第二个版本,如下所示:

在这里输入图像描述

数据模型版本2

不同之处在于:事件实体消失了,我们有了两个新的实体。 一个将时间戳存储为double ,第二个应该将date存储为NSString

目标是将所有版本1事件转移到两个新实体,并沿着迁移转换值。 这导致在两个值中的每一个作为不同types的单独实体。

为了迁移,我们select手动迁移,并且我们使用映射模型。 这也是你问题答案的第一部分。 我们将分两步进行迁移,因为迁移2k条目需要很长时间,我们希望保持较低的内存占用。

您甚至可以继续进一步拆分这些映射模型,以仅迁移实体的范围。 假设我们有一百万条logging,这可能会导致整个过程崩溃。 可以使用Filter谓词缩小取得的实体。

回到我们的两个映射模型。

我们创build如下的第一个映射模型:

1.新build文件 – >资源 – >映射模型 在这里输入图像描述

2.select一个名字,我select了StepOne

3.设置源和目标数据模型

在这里输入图像描述

映射模型第一步

在这里输入图像描述

在这里输入图像描述

在这里输入图像描述

多path迁移不需要自定义实体迁移策略,但是我们将这样做来获得这个例子更多的细节。 所以我们为这个实体添加一个自定义策略。 这总是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。

最后我们需要引发迁移。 现在我将跳过样板代码。 如果你需要的话,我会在这里发帖。 它可以在自定义迁移过程中find,它只是前两个代码示例的合并。 第三和最后一部分将被修改如下:而不是使用NSMappingModel类的类方法mappingModelFromBundles:forSourceModel:destinationModel:我们将使用initWithContentsOfURL:因为类方法将只返回一个,也许第一个find的映射模型捆绑。

现在我们已经得到了两种映射模型,可以在lopp的每一个过程中使用,并将迁移方法发送给迁移pipe理器。 而已。

 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结尾。

  • 目标商店必须提供,不应该是源存储。 您可以在成功迁移后删除旧的并重命名新的。

  • 在创build映射模型之后,我对数据模型做了一些更改,导致了一些兼容性错误,这只能通过重新创build映射模型来解决。

这些问题是相关的:

内存问题在iPhone上迁移大型CoreData数据存储

使用iOS的多块核心数据迁移块

引用第一个链接:

这在“Multiple Passes”部分的官方文档中已经讨论过了,不过看起来他们build议的方法是按照实体types划分你的迁移,也就是制作多个映射模型,每个映射模型都将一个实体types的子集从完整的数据模型。

假设你的数据库模式有5个实体,例如人,学生,课程,class级和注册使用标准类的例子,学生子类人员,class级实施课程,注册joinclass级和学生。 如果您对所有这些表定义进行了更改,则必须从基类开始,然后继续前进。 所以,你不能开始转换注册,因为每个注册logging取决于在那里有class级和学生。 因此,您只需要迁移Person表,将现有行复制到新表中,然后填写所有新的字段(如果可能)并放弃删除的列。 在一个自动释放池中进行每个迁移,这样一旦完成,你的内存就回来了。

Person表完成后,您可以转换学生表。 然后跳过课程,然后跳过Class,最后跳转到Registration表。

另一个考虑因素是logging的数量,如果像Person有一千行那样,你必须每100个左右执行一个版本的NSManagedObject等价物,这就是告诉被pipe理的对象上下文[moc refreshObject:ob mergeChanges:没有]; 同时设置陈旧的数据计时器的方式,以便内存经常刷新。