5

问题陈述

当尝试将记录保存到作为分配给同一 PersistentStoreCoordinator 的两个 SQLite 存储之一的读/写存储时,我的 iPhone 应用程序崩溃。保存记录时一个明显的问题是 PersistentStoreCoordinator 不知道将数据保存在哪个 Store 中(只是因为我不知道如何实现这一点)。

首先,我将提供总体情况以确保我的方法是合理的。然后我将提供实现细节。

背景

这是一个简化的示例,代表了我正在开发的实际应用程序的关键方面。

托管对象模型

种子数据

种子数据

用户输入场景

用户输入场景

当前实施

核心数据实现

核心数据实现

数据存储和检索

数据存储和检索

当然,在查看选择列表以对属性进行选择时,不应向用户提供任何证据表明选择来自两个不同的商店。

持久存储协调器设置

- (NSPersistentStoreCoordinator*)persistentStoreCoordinator {
   if (_persistentStoreCoordinator == nil) {
       NSArray *bundles = @[[NSBundle bundleForClass:[self class]]];
       _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[NSManagedObjectModel mergedModelFromBundles:bundles]];

       NSError *error;
       //--------------------------------------------------
       // Set options for the USER DATA Persistent Store.
       NSDictionary *options = @{NSMigratePersistentStoresAutomaticallyOption : @YES,
                                       NSInferMappingModelAutomaticallyOption : @YES};
       //--------------------------------------------------
       // Add the USER DATA Store to the Persistent Store Coordinator.
       NSPersistentStore *persistentStore = [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                                                   configuration:nil
                                                                                             URL:self.persistentStorePathForUserData
                                                                                         options:options
                                                                                           error:&error];
       //--------------------------------------------------
       // Set options for the SEED DATA Persistent Store.
       options = @{NSMigratePersistentStoresAutomaticallyOption : @YES,
                         NSInferMappingModelAutomaticallyOption : @YES,
                                NSReadOnlyPersistentStoreOption : @YES};
       //--------------------------------------------------
       // Add the SEED DATA Store to the Persistent Store Coordinator.
       persistentStore = [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                                configuration:nil
                                                                          URL:self.persistentStorePathForSeedData
                                                                      options:options
                                                                        error:&error];
   }
   return _persistentStoreCoordinator;
}

重要目标

请记住以下几点:

  1. 如果可能的话,我更愿意进行种子数据更新,而不必在幕后管理数据版本(即仅提供应用程序更新,只提供新的或更改的种子数据记录并以某种方式处理删除)或实现版本检查功能处理用户从版本 n 升级到 n+5 的情况的代码。
  2. 用户数据和种子数据不应包含两者之间的任何重复记录并使用相同的 ManagedObjectModel。所以从数据和模型的角度来看,应该没有必要合并两个商店或将一个商店迁移到另一个商店。

研究

在这种情况下, 将对象从多个存储保存到单个持久存储,这两个存储被合并,然后获取所有记录,剔除重复项并保存上下文。我希望不必合并然后检查数以千计的重复记录。(参见上面的重要目标#2。)

在这种情况下, 合并两个 iOS 核心数据持久存储的有效方法是什么?,某些实体是只读的,而其他实体是读/写的。在我的应用程序中,种子数据存储中的所有实体都是只读的,用户数据存储中的相同实体是读/写的。所以我认为迁移不适用。(参见上面的重要目标#2。)

在 Apple 的“Persistent Store Coordinator”下的Core Data Programming Guide中,图 4“高级持久性堆栈”显示了使用两个 Store 的 Core Data 实现,但同样,每个 Store 都配置有单独且不同的对象。在我的应用程序中,每个对象都出现在每个商店中。

此处提出的解决方案 Combining Two SQLite Stores Into One,相对于在不同存储中的对象之间没有关系的两个存储似乎是相关的,但没有提供与我实现的内容进行比较的细节。

我已经阅读了Marcus Zarra 撰写的 Core Data (2nd Edition) 的前三章,但他没有使用两个不需要迁移的商店。然而,第 3 章确实提供了一个非常清晰的版本控制示例。(它的复杂性使我达到了上面的重要目标#1。)

这个解决方案,默认情况下在 iPhone 的核心数据中使用哪个持久存储,建议使用 ManagedObjectModel 的多个配置,但每个实体都分配给一个且只有一个配置。我不确定这个解决方案如何,甚至是否可以推断到我的情况。

也许这里提出的解决方案是NSPersistentStoreCoordinator 具有两种类型的持久存储?,接近我需要的。不幸的是,只处理请求。我在 NSManagedObjectContext 类中没有看到类似于 NSFetchRequest 方法的方法 setAffectedStores 用于保存。

4

2 回答 2

2

感谢Ray WenderlichMic Pringle的推荐,Mic 提出了一个托管对象模型架构,通过它我能够在坚持我的目标的同时解决问题。解决方案的关键是利用抽象实体作为用户实体和种子实体的父实体。

托管对象模型

使用此架构,可以创建分配给单独存储的两个配置: 1) UserData - 位于用户文档目录中的 r/w 存储。

用户数据配置

2) SeedData - r 仅存储在 App Bundle 中。

种子数据配置

缺点是必须为种子数据实体维护记录 ID(因为配置之间不允许存在关系),但最大的好处是可以对种子数据进行更改或添加,而不会影响用户的条目,也不必采用该问题原始帖子的研究部分中讨论的任何繁琐的解决方案。

于 2014-08-21T20:19:57.570 回答
1

对于使用一个上下文/协调器和两个存储来保存数据的核心问题,您想要的方法是:

  1. 通过 添加用户可编辑存储时addPersistentStore,保存对NSPersistentStore返回对象的引用。
  2. 创建新对象以保存在用户可编辑存储中时,请执行以下操作:

    NSManagedObject *newObject = [NSEntityDescription insertNewObjectForEntityForName:@"Vehicle" inManagedObjectContext:self.managedObjectContext];
    [self.managedObjectContext assignObject:newObject toPersistentStore:userEditableStore];
    

    这里的关键是在保存更改assignObject:toPersistentStore: 之前显式调用。

相关问题:

如果可能的话,我更愿意进行种子数据更新,而不必在幕后管理数据版本......

如果您将不可编辑的商店保留在应用程序包中(即您没有将文件复制到其他地方),您可以在新版本的应用程序中包含新版本的数据。您将始终使用应用程序包中的任何版本,因此您将获得最新数据。

如果您最终将数据从种子存储复制到用户存储,请确保每个条目在添加时都包含应用程序(或种子存储)版本号。这样可以轻松避免重复。

用户数据和种子数据不应包含两者之间的任何重复记录并使用相同的 ManagedObjectModel。所以从数据和模型的角度来看,应该没有必要合并两个商店或将一个商店迁移到另一个商店。

如果您在不同存储中的对象之间没有任何关系,则不需要将数据从一个复制到另一个- 因为这在 Core Data 中是不允许的。如果您需要关系(听起来确实如此),请查看fetched properties。这些看起来很像实体类型上的属性或关系,但在内部,它们从持久存储中查找值。使用多个持久存储文件,这允许几乎但不完全类似于不同存储中的对象之间的关系。

于 2014-05-14T23:09:12.577 回答