如何将 undoManager 与核心数据实体一起使用
Posted
技术标签:
【中文标题】如何将 undoManager 与核心数据实体一起使用【英文标题】:How to use undoManager with a core data entity 【发布时间】:2012-04-24 18:46:23 【问题描述】:我有一个名为appointment
的NSManagedObject,我编辑了它的属性。如果我用户按下 cancel,我想撤销所有这些编辑。
如果我这样做(示例代码)
[[appointment managedObjectContext] setUndoManager:[[NSUndoManager alloc] init]]; //however doing a nslog on undoManager still shows it as (null);
[[[appointment managedObjectContext] undoManager] beginUndoGrouping];
appointment.startTime = 11;
appointment.endTime = 12;
appointment.customer = @"Tom";
[[[appointment managedObjectContext] undoManager] endUndoGrouping];
[[[appointment managedObjectContext] undoManager] undo];
难道不应该撤消beginUndoGrouping
和endUndoGrouping
之间的所有更改吗?似乎有很多方法可以做到这一点,但我似乎找不到正确的方法。在NSManagedObject
上撤消更改的正确方法是什么?
【问题讨论】:
【参考方案1】:我想这只是事件进行顺序的一个例子,而不是一个实际的例子。
您是否偶然忘记给 ManagedObjectContext 一个 NSUndoManager?
我相信你在 OS X 下默认会得到一个,但在 ios 下,你必须专门提供一个。
您要确保在创建 MOC 时设置撤消管理器...
managedObjectContext.undoManager = [[NSUndoManager alloc] init];
如果 undo-manager 为 nil,则在执行此操作后,您正在使用多个 MOC,或者某些其他代码已将其重置。
此外,出于调试目的,检查 Appointment.managedObjectContext 属性,并确保它不为 nil 并引用有效的 MOC。
编辑
好的,我刚刚使用一个简单的模型编写了一个快速测试。也许你应该做一些类似的事情来看看你的断言在哪里失败(你可以在你的代码路径中添加正常的断言——我把这个作为单元测试做了,所以我可以很容易地将它添加到现有的项目中)。
- (void)testUndoManager
NSDate *now = [NSDate date];
NSManagedObjectContext *moc = [self managedObjectContextWithConcurrencyType:NSConfinementConcurrencyType];
STAssertNil(moc.undoManager, @"undoManager is nil by default in iOS");
moc.undoManager = [[NSUndoManager alloc] init];
[moc.undoManager beginUndoGrouping];
NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:EVENT_ENTITY_NAME inManagedObjectContext:moc];
STAssertNotNil(moc, @"Managed Object is nil");
STAssertEquals(moc, object.managedObjectContext, @"MOC of object should be same as MOC");
STAssertNotNil(object.managedObjectContext.undoManager, @"undoManager of MOC should not be nil");
[object setValue:now forKey:@"timestamp"];
STAssertEqualObjects(now, [object valueForKey:@"timestamp"], @"Timestamp should be NOW");
[moc.undoManager endUndoGrouping];
STAssertEqualObjects(now, [object valueForKey:@"timestamp"], @"Timestamp should be NOW");
[moc.undoManager undo];
STAssertNil([object valueForKey:@"timestamp"], @"Object access should be nil because changes were undone");
编辑
托管对象的 MOC 可以在多种情况下设置为零。例如,如果您删除一个对象,然后保存该 mod,则该对象的 MOC 将设置为 nil...
NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:@"SomeEntity" inManagedObjectContext:moc];
[object.managedObjectContext deleteObject:object];
[moc save:0];
// object.managedObjectContext will be nil
另一种不太常见的情况,但表明 MOC 可能存在内存问题... 在 ARC 下,托管对象的 MOC 是弱指针。因此,如果 MOC 消失,该指针将被重置为零。在非 ARC 下,指针将只有旧值,您的结果将是未定义的......可能会崩溃。
所以,如果 managedObject.managedObjectManager 为 nil,最可能的罪魁祸首是:
-
对象从未插入到 MOC 中
对象已从 MOC 中删除
MOC 已删除
【讨论】:
是的,这是一个例子。实际上我确实做了一个 setUndoManager 但是它在这样做之后仍然是空的。 您是否在文档中提到了它在 iOS 下的说明,必须明确提供撤消管理器? OK 我的一个同事找到了参考:developer.apple.com/reference/coredata/nsmanagedobjectcontext/… 看起来 undoManager 创建的行为已根据 Sierra 发生了变化。现在,在实例化 NSManagedObjectContext 时,它不会像以前那样被默认创建和设置。现在看来,该行为与 iOS 相同,开发人员应在创建新的 NSManagedObjectContext 时显式创建和/或设置 NSUndoManager 的实例。【参考方案2】:撤消不适用于 Core Data 的最大原因是没有创建和设置撤消管理器...
newManager = [[[NSUndoManager alloc] init] autorelease];
[newManager setLevelsOfUndo:4];
myManagedObjectContext.undoManager = newManager;
您也不需要 Begin/end undoGrouping,因为这是为您完成的。
在您返回事件循环并在下次调用之前,撤消也有可能(部分原因是为您完成)。 (换句话说,减少用户点击撤消按钮可能不起作用。)
啊,你刚刚添加了上面的评论。请发布执行设置的代码,因为您的 undoManager 为 null 显然会使其无法正常工作。
【讨论】:
如果在你的 setUndoManager 之后,NSLog("%@",[[appointment managedObjectContext] undoManager]);显示为 nil,那么可能约会或约会.managedObjectContext 为 nil? 约会不是零,因为我在其他地方使用它的数据。我敢打赌 managedObjectContext 是零。我应该如何设置它?appointment.managedObjectContext = [[CoreDataHelper sharedInstance] managedObjectContext];
?
嗯,对象的MOC怎么没设置?它应该在 MOC 中创建,然后会自动设置。试试 appt、appt.moc 和 appt.moc.undomgr 的 NSlog
请注意,您不能只分配/初始化托管对象,您必须执行 [NSEntityDescription insertNewObjectForEntityForName: inManagedObjectContext: ]
我编辑了我的答案以包括一些对象的 MOC 可能为零的场景。以上是关于如何将 undoManager 与核心数据实体一起使用的主要内容,如果未能解决你的问题,请参考以下文章
将 mogenerator 与 Core Data 实体一起使用会导致保存数据存储时出错