【发布时间】:2016-05-21 05:46:57
【问题描述】:
核心数据支持开箱即用的撤消/重做。但它的行为出乎意料。
为了使我的用户界面与我的模型保持同步,我会发送通知。我的用户界面收到通知消息并更新受影响的视图。
@objc(Entity)
class Entity : NSManagedObject
{
var title : String? {
get {
self.willAccessValueForKey("title")
let text = self.primitiveValueForKey("title") as? String
self.didAccessValueForKey("title")
return text
}
set {
self.willChangeValueForKey("title")
self.setPrimitiveValue(newValue, forKey: "title")
self.didChangeValueForKey("title")
self.sendNotification(self, key:"title")
print("title did change: \(title)")
}
}
}
现在我想为应用添加撤消/重做支持。核心数据有一个 NSUndoManager,所以我认为不需要额外的工作。或者至少不多。为了测试这个假设,我制作了一个包含两个 NSTextField 和一个核心数据实体(恰当地命名为实体)的测试应用程序。
NSViewController 子类可以访问实体实例(恰当地命名为 testObject)。我通过 controlTextDidChange: 观察每次击键以更新 testObject。
override func controlTextDidChange(obj: NSNotification)
{
guard let value = self.textField?.stringValue else { return }
self.testObject?.setValue(value, forKey: "title")
}
func valueDidChange(sender: Entity, key: String)
{
self.textField?.stringValue = sender.valueForKey("title") as? String ?? ""
}
managedObjectContent 和两个 textField 具有相同的 NSUndoManager(调试控制台中的相同指针)。
当我编辑 NSTextField 并执行撤消/重做操作时,NSTextField 和底层 NSManagedObject 属性保持同步。正如预期的那样。
但是当我将焦点(第一响应者)更改为第二个 NSTextField(没有任何编辑)和撤消/重做操作时,第一个 NSTextField (正确)更新但底层 NSManagedObject 属性没有。 title 属性永远不会被调用。
所以第一个 NSTextField 和 Entity 实例在撤消/重做操作后具有不同的值。
更新底层核心数据实例而不是用户界面对我来说更有意义。这里出了什么问题?
旁注:因为我正在观察 NSManagedObject 的任何更改,并且因为 controlTextDidChange: 正在发送通知(因为它正在更新 NSManagedObject),所以我收到了对 valueDidChange 的不必要调用时间>。有什么技巧可以避免这种情况,或者我该如何改进我的架构?
【问题讨论】:
-
你试过回滚吗?
-
NSObjectController 和绑定可以为你做到这一点。您唯一要做的就是设置对象控制器的内容。
-
与您的问题无关,但与其做的所有获取/设置的东西,不如直接做:didSet { self.sendNotification(...) }
-
哦,coredata 仍然停留在 obj-c 中,并且对 swift enums 和 struct 的支持并不是那么好。所以我有支持这些类型的自定义设置器/获取器。这个 sn-p 可能只是复制/粘贴这些功能来澄清我的问题。具体我记不太清了。但是,是的,didSet 在正常情况下也可以正常工作。这取决于您架构的其余部分。
标签: cocoa user-interface core-data swift2 nsundomanager