【问题标题】:Core Data: How to Create a Persistent Document with Singleton Entity?核心数据:如何使用单例实体创建持久文档?
【发布时间】:2016-04-15 23:20:53
【问题描述】:

我有一个 swift 应用程序,它使用核心数据来存储文档。每个文档都包含一个实体Settings,每个文档中都应存在该实体的一个实例。

应该为每个新文档自动创建实例,并且在打开现有文档时可以访问。

我应该如何实现这样一个核心数据应用程序?

【问题讨论】:

    标签: swift design-patterns core-data


    【解决方案1】:

    实现此类单例实体的最可靠方法是使用特殊的 getter,它会在需要时检查并创建实体。

    class Document: NSPersistentDocument {
        var settings: Settings {
            if _settings == nil {
                do {
                    let fetchSettings = NSFetchRequest(entityName: "Settings")
                    let settingsList = try self.managedObjectContext!.executeFetchRequest(fetchSettings)
                    precondition(settingsList.count < 2, "Too many settings object in the core data store.")
                    if settingsList.count == 1 {
                        self._settings = settingsList[0] as? Settings
                        precondition(self._settings != nil)
                    } else {
                        self._settings = NSEntityDescription.insertNewObjectForEntityForName("Settings", inManagedObjectContext: self.managedObjectContext!) as? Settings
                        precondition(self._settings != nil)
                        // init the settings object here
                        self.managedObjectContext!.processPendingChanges()
                        self.managedObjectContext!.undoManager!.removeAllActions()
                    }
                } catch {
                    preconditionFailure("Could not retrieve/create settings object because of an unknown core data error.")
                }
            }
            return _settings!
        }
        private var _settings: Settings?
        // ...
    }
    

    此示例中的类SettingsNSManagedObject 的子类,以简化对对象值的访问。您可以直接使用NSManagedObject 而不是自己的类。

    Document 有一个私有属性_settings,它保存Settings 实体的当前实例。 getter settings 检查这个变量,如果没有设置实例,则检查实体的托管对象上下文。

    let fetchSettings = NSFetchRequest(entityName: "Settings")
    let settingsList = try self.managedObjectContext!.executeFetchRequest(fetchSettings)
    

    首先从上下文中获取所有Settings 实体的列表。

    if settingsList.count == 1 {
        self._settings = settingsList[0] as? Settings
        precondition(self._settings != nil)
    } else {
    

    如果已经存在Settings 实体,则会检查此列表。在这种情况下,使用现有实体。

    self._settings = NSEntityDescription.insertNewObjectForEntityForName("Settings", inManagedObjectContext: self.managedObjectContext!) as? Settings
    precondition(self._settings != nil)
    // init the settings object here
    

    如果不存在Settings 实体,则会创建一个新实体并使用默认值进行初始化。

    self.managedObjectContext!.processPendingChanges()
    self.managedObjectContext!.undoManager!.removeAllActions()
    

    这两行删除了为初始化新的Settings 实体而创建的任何撤消操作。有必要确保新文档不以“已编辑”标志开头。它还确保初始撤消不会删除创建的Settings 对象。

    在完成任何用户交互之前尽快访问settings 属性非常重要。否则界面将有撤消操作的问题。最好是访问文档窗口NSWindowControllerdocument 属性中的settings getter:

    override var document: AnyObject? {
        didSet {
            // access the `settings` property of the document.
        }
    }
    

    为什么不把所有东西都放进Document.init()

    可以在init() 方法中为文档初始化数据,但此时托管对象上下文仍为空。您必须等到文档的初始化完成,然后才能检查托管对象上下文中是否存在Settings 对象。

    可以禁用撤消而不是删除所有操作吗?

    这实际上取决于应用程序的其余实现。但是使用这样的代码很容易:

    self.managedObjectContext!.processPendingChanges()
    self.managedObjectContext!.undoManager!.disableUndoRegistration()
    self._settings = NSEntityDescription.insertNewObjectForEntityForName("Settings", inManagedObjectContext: self.managedObjectContext!) as? Settings
    precondition(self._settings != nil)
    // init the settings object here
    self.managedObjectContext!.processPendingChanges()
    self.managedObjectContext!.undoManager!.enableUndoRegistration()
    

    【讨论】:

    • 可以暂时禁用撤消而不是删除撤消操作。可以稍后创建设置对象。
    • @Willeke 是的,这是可能的。我在答案中添加了一些细节。结果实际上取决于您的应用程序的其余部分。重要的是两个processPendingChanges(),以确保按顺序处理所有内容。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-30
    • 1970-01-01
    • 2011-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-11
    相关资源
    最近更新 更多