核心数据:如何使用Singleton实体创建持久文档



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

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

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

实现这种单例实体最可靠的方法是使用一个特殊的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()

最新更新