多次通过核心数据迁移的示例或说明?

时间:2011-05-13 16:50:48

标签: iphone objective-c ios core-data core-data-migration

我的iPhone应用程序需要迁移其核心数据存储,而且一些数据库非常庞大。 Apple's documentation suggests using "multiple passes" to migrate data to reduce memory use.但是,文档非常有限,并没有很好地解释如何实际执行此操作。有人可以指出我一个好的例子,或详细解释如何实际解决这个问题的过程吗?

3 个答案:

答案 0 :(得分:171)

我已经弄明白了苹果在documentation中提示的内容。它实际上非常简单,但在它显而易见之前还有很长的路要走。我将用一个例子说明解释。最初的情况是:

数据模型版本1

enter image description here enter image description here

这是使用“基于导航的应用程序和核心数据存储”模板创建项目时获得的模型。我编译了它,并在for循环的帮助下做了一些努力,创建了大约2k个条目,所有条目都有一些不同的值。我们在那里有2000个具有NSDate值的事件。

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

enter image description here

数据模型版本2

不同之处在于:Event实体已经消失,我们有两个新实体。一个将时间戳存储为double,另一个将时间存储为NSString

目标是将所有版本1 事件传输到两个新实体,并在迁移过程中转换值。这导致两个值作为单独实体中的不同类型的两倍。

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

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

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

我们创建第一个这样的映射模型:

1。新文件 - >资源 - >映射模型 enter image description here

2。选择一个名称,我选择了StepOne

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

enter image description here

映射模型第一步

enter image description here

enter image description here

enter image description here

多次传递迁移不需要自定义实体迁移策略,但是我们将这样做以获得此示例的更多详细信息。因此,我们向实体添加自定义策略。这始终是NSEntityMigrationPolicy的子类。

enter image description here

此策略类实现了一些方法来实现迁移。但是在这种情况下它很简单,所以我们只需要实现一种方法: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。

最后,我们需要触发迁移。我现在暂时跳过样板代码。如果您需要,我会在这里发布。它可以在Customizing the Migration Process找到,它只是前两个代码示例的合并。第三部分和最后一部分将被修改如下:我们将使用NSMappingModel而不是mappingModelFromBundles:forSourceModel:destinationModel:initWithContentsOfURL:的类方法,因为类方法只返回一个,也许是首先,在捆绑中找到映射模型。

现在我们有了两个映射模型,可以在循环的每次传递中使用,并将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结尾。

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

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

答案 1 :(得分:3)

这些问题是相关的:

Memory issues migrating large CoreData datastores on iPhone

Multiple Pass Core Data Migration In Chunks With iOS

引用第一个链接:

  

这在官方讨论   “多次通过”中的文档   部分,但它看起来像他们的   建议的方法是分开   您按实体类型迁移,即   制作多个映射模型   迁移实体的子集   完整数据模型中的类型。

答案 2 :(得分:-5)

假设您的数据库架构有5个实体,例如使用标准种类的例子的人,学生,课程,课程和注册,其中学生亚类人,班级实施课程,注册加入班级和学生。如果您对所有这些表定义进行了更改,则必须从基类开始,然后逐步完成。所以,你不能从转换注册开始,因为每个注册记录都取决于那里的班级和学生。因此,您将首先仅迁移Person表,将现有行复制到新表中,并填写任何新字段(如果可能)并丢弃已删除的列。在自动释放池中进行每次迁移,以便一旦完成,您的内存就会重新开始。

完成Person表后,您可以转换学生表。然后跳到Course然后是Class,最后是Register表。

另一个考虑因素是记录的数量,如果Person有一千行,你必须每100个左右执行NSManagedObject等同于一个版本,这是告诉托管对象的上下文[moc refreshObject: ob mergeChanges:NO]; 还要将过时的数据计时器设置为低,以便经常刷新内存。