一个对象模型能否同时使用两个持久化存储并保持它们之间的关系?

42

介绍

我的iOS项目使用Core Data持久存储,大小为160MB,格式为SQLite。里面有大量分组信息,用户应该能够标记收藏。为此,我需要(至少部分)数据库具有写入功能。但是,当然,应用程序包中的持久存储是只读的。

如果您想让存储具有读写功能,则应将其复制到例如应用程序的文档文件夹中。我不想这样做,因为那样会使应用程序的大小增加一倍,而那个数据库的主要部分仍然是只读的。这将是一种浪费资源的行为。

NSPersistentStoreCoordinator多个持久存储

这就是为什么我考虑使用两个持久存储的原因。第一个是应用程序包中的大型存储,第二个可以是文档文件夹中的小型存储,存储与大型存储相关的特殊“收藏”实体。

我知道在这方面可能有一些方法,但我找不到具体的说明。如果您只有多个对象模型,是否应该只使用多个存储?一个对象模型是否可以“分布”在两个持久存储上?在浏览Core Data编程文档时,我找不到有关如何设置此项的任何真实参考。Marcus Zarra的书似乎也没有深入探讨这个主题:

可以将多个NSPersistentStore添加到NSPersistentStoreCoordinator中,这在处理分成多个文件的数据时非常有用。但是,在我们的例子中,我们只有一个文件。(Marcus Zarra: "Core Data - Apple's API for Persisting Data on Mac OS X" page 71) 问题 谁能告诉我,使用Core Data和多个持久存储是否可行?你可以提供一些关于如何实现的提示吗?在线/离线资源,涉及该主题也很受欢迎。

我差不多想出来了,参见http://stackoverflow.com/questions/10951844/fetched-properties-predicate示例项目。 - Thomas T
你把它搞得太复杂了。将数据库从捆绑包复制到库文件夹中,然后就完成了。 - Rog
2
考虑到你要么对一个两年前的问题进行评论,要么对那个问题上一年的评论进行评论,@Rog 在这里并不是非常具有建设性。该应用程序的数据库现在接近 250 兆字节,其中我认为它的用户只希望它存在一次。 - epologee
3个回答

48
答案是肯定的。@Caleb指出了正确的资源,但是让它工作起来仍然相当麻烦。我想在这里放一个简历:
为了使两个NSPersistentStore实例共享同一个模型,您必须向模型添加一个配置,该配置是实体的字符串命名子集:

Model configurations

在模型中,对于属于第二个存储的实体,您可以添加一个获取属性(NSFetchedPropertyDescription 以便更好地进行搜索)。这有点像一个非常简单的存储过程,可能看起来像这样:

NSPredicate format for the fetched property

然后,当您将存储添加到持久性存储协调器时,您需要使用configuration参数的字符串(有关选项的更多信息在此处):

[persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType 
                                          configuration:@"ModifyInBackground" 
                                                    URL:storeURL1
                                                options:options
                                                  error:&error]

[persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType 
                                          configuration:@"ModifyInMain"
                                                    URL:storeURL2 
                                                options:options 
                                                  error:&error]

最后,当你想从存储B中的实体获取到存储A中的实体时,只需像触发故障一样访问获取的属性即可。

注意:获取的属性总是返回一个NSArray,因为你编写的用于建立链接的谓词可能有多个结果。如果你想获取一个实体,你可以在NSManagedObject子类的包装方法中放置类似以下内容的代码:

Wallpaper *recordedWallpaper = [record.wallpaper lastObject];

6
您太棒了,一段代码和两张图片比苹果的文档更能说明问题,谢谢! - CarmeloS
3
虽然 StackOverflow 应该得到真正的赞扬,但你的帮助令我感激不尽,谢谢! - epologee
快速提问:在您发布的 addPersistentStoreWithType:... 代码中,options 是什么?我是从模板构建我的应用程序,它只将 nil 发送到 options:。编辑:找到答案了 - GeneralMike
请参见 https://developer.apple.com/LIBRARY/IOS/documentation/Cocoa/Reference/CoreDataFramework/Classes/NSPersistentStoreCoordinator_Class/NSPersistentStoreCoordinator.html#//apple_ref/doc/constant_group/Store_Options类似于:NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption: @YES, NSInferMappingModelAutomaticallyOption: @YES }; - epologee

6

是的,您可以为单个模型使用多个存储区,但不能在不同的存储区之间创建对象关系。请参阅Core Data编程指南中的交叉存储区关系部分,其中基本上说明了这一点,并建议如果需要将一个存储区中的对象与另一个存储区中的对象关联,则应使用获取属性。


从文档中很难获得一个可工作的示例。根据描述,这是正确的方法。但在这种情况下,模型看起来像什么?您是否也使用两个不同的托管对象模型?还是一个模型带有两个存储? - epologee
在模型编辑器中,您如何指定哪些实体属于哪个存储?它是否完全按照这种方式工作? - epologee

2
一个想法:您可能希望创建完全不同的存储,以及每个存储的不同持久性存储协调器。然后为每个模型部分创建不同的托管对象上下文。假设我有一个包含3个实体的模型:学生、学院和课程。假设我想将学生和学院实体存储在store1中,而将课程存储在Store2中,我将拥有2组托管对象上下文、持久化存储和持久化协调器。现在,鉴于我们不能有任何跨存储关系,一个上下文中的修改不会影响另一个上下文。您不必创建不同的模型或将它们关联到存储等。

是的,那就是我最终所做的事情。问题在于,我希望能够使用Xcode建模工具来获得应用程序使用的所有数据的完整概述。但是我没有成功。 :S - epologee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接