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

Jas*_*son 85 iphone core-data objective-c core-data-migration ios

我的iPhone应用程序需要迁移其核心数据存储,而且一些数据库非常庞大.Apple的文档建议使用"多次传递"来迁移数据以减少内存使用.但是,文档非常有限,并没有很好地解释如何实际执行此操作.有人可以指出我一个好的例子,或者详细解释如何实际解决这个问题的过程吗?

Nic*_*ver 172

我已经弄明白了Apple在他们的文档中暗示了什么.它实际上非常简单,但在它显而易见之前还有很长的路要走.我将用一个例子说明解释.最初的情况是这样的:

数据模型版本1

在此输入图像描述 在此输入图像描述

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

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

在此输入图像描述

数据模型版本2

区别在于:Event实体已经消失,我们有两个新的实体.一个存储时间戳作为a double和第二个应存储日期的时间戳NSString.

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

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

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

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

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

1.新文件 - >资源 - >映射模型 在此输入图像描述

2.选择一个名字,我选择了StepOne

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

在此输入图像描述

映射模型第一步

在此输入图像描述

在此输入图像描述

在此输入图像描述

多次传递迁移不需要自定义实体迁移策略,但是我们将这样做以获得此示例的更多详细信息.因此,我们向实体添加自定义策略.这始终是子类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;
}
Run Code Online (Sandbox Code Playgroud)

最后一步:迁移本身

我将跳过设置第二个映射模型的部分,它几乎完全相同,只是用于将NSDate转换为double的timeIntervalSince1970.

最后,我们需要触发迁移.我暂时跳过样板代码.如果您需要,我会在这里发布.它可以在Customizing the Migration Process中找到,它只是前两个代码示例的合并.如下第三和最后一部分将被修改:除了使用的类方法的NSMappingModelmappingModelFromBundles: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];
} 
Run Code Online (Sandbox Code Playgroud)

笔记

  • 映射模型以cdm捆绑结束.

  • 必须提供目标存储,不应该是源存储.成功迁移后,您可以删除旧版本并重命名新版本.

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

  • 血淋淋的地狱那很复杂.Apple的想法是什么? (58认同)
  • 我不知道,但每当我认为核心数据是一个好主意时,我会努力寻找一种更简单,更易维护的解决方案. (7认同)
  • 谢谢!这是一个极好的答案.这似乎很复杂,但是一旦你学会了这些步骤,它就不那么糟糕了.最大的问题是文档没有像这样拼出来. (5认同)
  • 这是自定义迁移过程的更新链接.自撰写这篇文章以来它已经发生了变化.https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/vmCustomizing.html#//apple_ref/doc/uid/TP40004399-CH8-SW1 (2认同)