核心数据 - 混合轻量级和自定义迁移

时间:2014-08-13 11:34:02

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

所以我在App Store中拥有我的Core Data应用程序版本1,现在我开始处理第2版了。

我对我的数据库模型进行了一些小的更改,并且我需要在完成从版本1升级到版本2的更改之后运行一些自定义代码。

我可以使用Core Data的轻量级迁移来处理模型更改,并且我可以在迁移完成后运行自定义代码。

问题是,我不确定将来会发生什么,我将构建版本3,4,5 ......

假设情况如此:
版本1到版本2 - 使用轻量级迁移
版本2到版本3 - 使用带模型映射的自定义迁移
版本3到版本4 - 再次使用轻量级迁移
等等..

我不确定如何构建一种处理轻量级和自定义迁移混合的机制 我在网上或在核心数据文档中找不到任何谈论这个问题的代码,我的意思是对于大多数核心数据应用程序来说这是一个非常常见的问题,是否有针对此问题的最佳实践示例?

2 个答案:

答案 0 :(得分:1)

如果您可以从服务器或类似的东西重新获取数据,处理此问题的最佳方法是删除旧模型并立即重新创建具有当前结构的新模型。它嵌入在单例类中,每次必须创建新的managedobjectcontext时都会调用它。我的代码如下:

- (NSManagedObjectContext *)managedObjectContext {
    if (__managedObjectContext != nil) {
        return __managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        __managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        [__managedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return __managedObjectContext;
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    if (__persistentStoreCoordinator != nil) {
        return __persistentStoreCoordinator;
    }

    NSURL *storeURL = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:@"YOURDB.sqlite"];

    NSError *error = nil;
    __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
        if (error.code == 134100) {
            if ( [[NSFileManager defaultManager] fileExistsAtPath: [storeURL path]] ) {
                NSDictionary *existingPersistentStoreMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType: NSSQLiteStoreType URL: storeURL error: &error];
                if ( !existingPersistentStoreMetadata ) {
                    // Something *really* bad has happened to the persistent store
                    [NSException raise: NSInternalInconsistencyException format: @"Failed to read metadata for persistent store %@: %@", storeURL, error];
                }

                if ( ![[self managedObjectModel] isConfiguration: nil compatibleWithStoreMetadata: existingPersistentStoreMetadata] ) {
                    if (![[NSFileManager defaultManager] removeItemAtURL: storeURL error: &error] ) {
                        NSLog(@"*** Could not delete persistent store, %@", error);
                        abort();
                    } else {
                        [__persistentStoreCoordinator addPersistentStoreWithType: NSSQLiteStoreType configuration: nil URL: storeURL options: nil error: &error];
                    }
                }
            }
        } else {
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }

    return __persistentStoreCoordinator;
}

答案 1 :(得分:0)

最简单的方法是将旧属性保留在新版本中,然后不再使用它们。如果需要将它们映射到新属性,只需在轻量级数据库升级后进行映射即可。世界将保持和平。

好处:

  1. 不需要建立映射模型。
  2. 保持轻量化升级。

让我给你举个例子: 假设我们有一个名为Student的实体,在V1中具有属性名称,我们将其映射到V2中的新属性firstName。 我们提供了一种如下所示的方法,因此,在常规的轻量级CoreData升级后,我们可以调用此方法,尽情享受吧!

- (void)migrateStudentRecords {
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Student"];
    NSArray *result = [defaultContext executeFetchRequest:fetchRequest error:error];
    if (result && result.count > 0) {
        [result enumerateObjectsUsingBlock:^(Student  *_Nonnull newRecord, NSUInteger idx, BOOL * _Nonnull stop) {
            newRecord.firstName = newRecord.name;
            newRecord.name = nil;
        }];
        if (defaultContext.hasChanges) {
            [defaultContext save:error];
        }
    }
}