我的iPhone应用程序需要迁移其核心数据存储,而且一些数据库非常庞大。 Apple's documentation suggests using "multiple passes" to migrate data to reduce memory use.但是,文档非常有限,并没有很好地解释如何实际执行此操作。有人可以指出我一个好的例子,或详细解释如何实际解决这个问题的过程吗?
答案 0 :(得分:171)
我已经弄明白了苹果在documentation中提示的内容。它实际上非常简单,但在它显而易见之前还有很长的路要走。我将用一个例子说明解释。最初的情况是:
这是使用“基于导航的应用程序和核心数据存储”模板创建项目时获得的模型。我编译了它,并在for循环的帮助下做了一些努力,创建了大约2k个条目,所有条目都有一些不同的值。我们在那里有2000个具有NSDate值的事件。
现在我们添加第二个版本的数据模型,如下所示:
不同之处在于:Event实体已经消失,我们有两个新实体。一个将时间戳存储为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。
最后,我们需要触发迁移。我现在暂时跳过样板代码。如果您需要,我会在这里发布。它可以在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]; 还要将过时的数据计时器设置为低,以便经常刷新内存。