多次传递的标准核心数据迁移不起作用

Posted

技术标签:

【中文标题】多次传递的标准核心数据迁移不起作用【英文标题】:Standard core data migration with multiple passes not working 【发布时间】:2012-09-23 13:00:50 【问题描述】:

我在这里学习了关于核心数据标准迁移的教程:

http://mipostel.com/index.php/home/70-core-data-migration-standard-migration-part-2

然后在这里进行多次传递:

Example or explanation of Core Data Migration with multiple passes?

这里给了我结果代码:

- (NSManagedObjectContext *)managedObjectContext 

    if (managedObjectContext != nil) 
        return managedObjectContext;
    

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) 
        managedObjectContext = [TICDSSynchronizedManagedObjectContext new];
        [managedObjectContext setPersistentStoreCoordinator: coordinator];
    
    return managedObjectContext;

- (NSManagedObjectModel *)managedObjectModel 

    if (managedObjectModel != nil) 
        return managedObjectModel;
    

    //    managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
    NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"EntryDatabase" ofType:@"momd"];
    NSURL *modelURL = [NSURL fileURLWithPath:modelPath];
    managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

    return managedObjectModel;

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator 

    if (persistentStoreCoordinator != nil) 
        return persistentStoreCoordinator;
    

    NSString *storePath = [[self applicationDocumentsDirectory]
                           stringByAppendingPathComponent:@"CoreDataStore.sqlite"];

    NSFileManager *fileManager = [NSFileManager defaultManager];
    // If the expected store doesn't exist, copy the default store.
    NSLog(@"file exists at path: %@, %i", storePath, [fileManager fileExistsAtPath:storePath]);
    if (![fileManager fileExistsAtPath:storePath]) 
        NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:@"CoreDataStore" ofType:@"sqlite"];
        if (defaultStorePath) 
            [fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
        
    

    persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];

    NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
    NSError *error;
    NSDictionary *pscOptions = [NSDictionary dictionaryWithObjectsAndKeys:
                                [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                                [NSNumber numberWithBool:NO], NSInferMappingModelAutomaticallyOption,
                                nil];

    if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                  configuration:nil
                                                            URL:storeUrl
                                                        options:pscOptions
                                                          error:&error]) 
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    

    return persistentStoreCoordinator;


- (BOOL)checkForMigration

    BOOL migrationSuccess = NO;
    NSString *storeSourcePath = [[self applicationDocumentsDirectory]
                                 stringByAppendingPathComponent:@"CoreDataStoreNew.sqlite"];
    NSFileManager *fileManager = [NSFileManager defaultManager];

    if (![fileManager fileExistsAtPath:storeSourcePath]) 
        //Version 2 SQL has not been created yet, so the source is still version 1...
        storeSourcePath = [[self applicationDocumentsDirectory]
                           stringByAppendingPathComponent:@"CoreDataStore.sqlite"];
    

    NSURL *storeSourceUrl = [NSURL fileURLWithPath: storeSourcePath];
    NSError *error = nil;
    NSDictionary *sourceMetadata = [NSPersistentStoreCoordinator
                                    metadataForPersistentStoreOfType:NSSQLiteStoreType
                                    URL:storeSourceUrl
                                    error:&error];
    if (sourceMetadata) 
        NSString *configuration = nil;
        NSManagedObjectModel *destinationModel = [self.persistentStoreCoordinator managedObjectModel];

        //Our Source 1 is going to be incompatible with the Version 2 Model, our Source 2 won't be...
        BOOL pscCompatible = [destinationModel isConfiguration:configuration compatibleWithStoreMetadata:sourceMetadata];
        NSLog(@"Is the STORE data COMPATIBLE? %@", (pscCompatible==YES) ?@"YES" :@"NO");

        if (pscCompatible == NO) 
            migrationSuccess = [self performMigrationWithSourceMetadata:sourceMetadata toDestinationModel:destinationModel];
        
    
    else 
        NSLog(@"checkForMigration FAIL - No Source Metadata! \nERROR: %@", [error localizedDescription]);
    
    return migrationSuccess;



- (BOOL)performMigrationWithSourceMetadata :(NSDictionary *)sourceMetadata
                         toDestinationModel:(NSManagedObjectModel *)destinationModel



    BOOL migrationSuccess = NO;
    //Initialise a Migration Manager...
    NSManagedObjectModel *sourceModel = [NSManagedObjectModel mergedModelFromBundles:nil
                                                                    forStoreMetadata:sourceMetadata];
    //Perform the migration...
    if (sourceModel) 
        NSMigrationManager *standardMigrationManager = [[NSMigrationManager alloc]
                                                        initWithSourceModel:sourceModel
                                                        destinationModel:destinationModel];

        NSArray *mappingModelNames = [NSArray arrayWithObjects:@"StepOne", @"StepTwo", nil];
        NSDictionary *sourceStoreOptions = nil;

        NSString *destinationStorePath = [[self applicationDocumentsDirectory]
                                          stringByAppendingPathComponent:@"CoreDataStoreNew.sqlite"];

        NSURL *destinationStoreURL = [NSURL fileURLWithPath: destinationStorePath];

        NSString *destinationStoreType = NSSQLiteStoreType;

        NSDictionary *destinationStoreOptions = nil;

        for (NSString *mappingModelName in mappingModelNames) 

            NSError *error;

            NSURL *fileURL = [[NSBundle mainBundle] URLForResource:mappingModelName withExtension:@"cdm"];

            NSMappingModel *mappingModel = [[NSMappingModel alloc] initWithContentsOfURL:fileURL];

            migrationSuccess = [standardMigrationManager migrateStoreFromURL:destinationStoreURL
                                                                        type:NSSQLiteStoreType
                                                                     options:sourceStoreOptions
                                                            withMappingModel:mappingModel
                                                            toDestinationURL:destinationStoreURL
                                                             destinationType:destinationStoreType
                                                          destinationOptions:destinationStoreOptions
                                                                       error:&error];

            NSLog(@"Error: %@", error);
        

    

    return migrationSuccess;


不过,应用程序内存不足并在persistentStoreCoordinator 中的行崩溃:

if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                              configuration:nil
                                                        URL:storeUrl
                                                    options:pscOptions
                                                      error:&error]) 

【问题讨论】:

你之前不是也发过同样的问题吗?如果不是其他人发布了几乎完全相同的内容。我现在确实有一个想法,我以前没有,因为我使用的技术也应该在这里工作(但它不是微不足道的)将在文本编辑器中编写,然后稍后添加。 @DavidH 我已经就同一代码提出了其他问题。期待看到您的替代方案。 【参考方案1】:

我记得我也学过这个教程。

其实我发现这不是正确的做法。顺便说一句,您是否阅读了该教程的最后一条评论并尝试删除提到的行?也许这可以解决您的问题?

无论如何,就我而言,我意识到(在遵循本教程之后,修改没有帮助)有一个更简单的方法。

但是,在您的情况下,我不知道,如果这个小小的修改不能解决它,我真的会寻找另一个参考 - 因为这确实不是正确的做法,并且会引导您走向错误的方向,因为它尝试自己做所有事情,而不是使用 Apple 核心数据迁移过程背后的逻辑,因为我发现了困难的方法。

【讨论】:

确实删除了这些行。您发现了哪种替代方案? 事实证明,我的情况很简单——所以我所要做的就是使用添加了所有新属性和关系的正常迁移——我不确定它是否适用于你。抱歉,但我能给出的唯一建议是真正重新检查您的新数据模型,看看您是否真的需要更复杂的翻译...... - 就我而言(在我了解轻量级可以做什么之后)这就是我所需要的【参考方案2】:

我几年前解决的问题本质上是从一个存储库中取出一个子树并将其复制到另一个存储库中,而我所做的应该对你有用。我是在 Mac 上执行此操作的,因此内存不是问题,但通过按照核心数据编程指南“减少内存开销”进行适当的故障排除和减少内存,您应该能够使其正常工作。

以下解决方案基于 MOM 并非完全不同的假设。让我介绍一下现有上下文的术语“A”和新上下文的“B”。

1) 第一步是将 A 中的每个对象复制到 B 中。如果类保持不变,那很好。这意味着对于每个对象,您都需要一个来自实体的所有值的列表。我建议使用键 - 每个实体类型的属性键数组,这将使编码更容易(如果可以的话)。否则,您实际上可以从 MOM 获取密钥——这就是我在旧代码中所做的。

现在是关键步骤 - 您必须创建一个翻译词典(也许您需要两个 - 我做了),对于 A 中的每个实体,您都知道“B”中的对应一个。您可以使用“objectID”属性(但对于 B,请不要保存直到完成,因为一旦保存此值更改)。

2) 现在您已经完全重新创建了所有实体,您需要将它们“连接”起来 - 这样所有的关系都已正确设置。同样,为每个实体类型创建一些键数组,然后在循环中查看“A”中的每个关系,获取它指向的实体,使用转换表在“B”中找到对应的实体,然后设置'B' 中的值。

瞧!完成。您显然是添加是删除,您是否执行上述操作以反映 A - B 的更改。

再一次,我不需要太担心 Mac 上的内存问题,因此不需要任何技巧来降低内存。我相信故障处理 ('refreshObject:mergeChanges:') 会帮助您,但再也不必这样做了(即便如此,可能只是尺寸较大的对象)。

【讨论】:

以上是关于多次传递的标准核心数据迁移不起作用的主要内容,如果未能解决你的问题,请参考以下文章

核心数据 - 轻量级迁移不起作用

swift 3中的核心数据轻量级迁移不起作用

启用迁移不起作用

Grails 数据库迁移插件 updateOnStart 不起作用

UIView 核心动画在 viewdidLoad 方法中不起作用?

Laravel 迁移不起作用