子上下文中与 NSManagedObject 的绑定仅适用于新对象
背景:
在我的应用中,我专门针对 Mac OS X Lion。此问题涉及 Core Data、NSPopover
和子 NSManagedObjectContext
(通过使用 NSManagedObjectContext
的新 ParentContext 属性创建)。
我有一个“Location”类的 NSManagedObjects
表。有一个调用 addLocation: 的“添加”按钮,如果双击表行,我会调用 tableViewDoubleClick:。
对于任何一种情况,我所做的都是创建一个新的 NSManagedObjectContext 并将其父上下文设置为文档上下文的父上下文。然后,我要么在该上下文中创建一个新位置,要么从临时上下文中获取要编辑的位置。我将弹出窗口的representedObject 属性设置为有问题的位置。如果我取消弹出窗口,则不会保存任何内容。如果用户单击弹出窗口中的“保存”按钮,我只需在临时上下文上调用 save: ,更改就会推送到主上下文。
addLocation:
- (IBAction)addLocation:(id)sender
{
LocationEditViewController *popupController = [[[LocationEditViewController alloc] init] autorelease];
popupController.title = @"Add New Location";
NSManagedObjectContext *tempContext = [[[NSManagedObjectContext alloc] init] autorelease];
tempContext.parentContext = self.document.managedObjectContext;
Location *tempLocation = [NSEntityDescription insertNewObjectForEntityForName:@"Location" inManagedObjectContext:tempContext];
popupController.representedObject = tempLocation;
popupController.managedObjectContext = tempContext;
[popupController.popover showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMaxYEdge];
}
tableViewDoubleClick:
- (void)tableViewDoubleClick:(id)sender
{
NSInteger selectedRow = [self.table selectedRow];
if (selectedRow != -1)
{
NSRect rectOfSelectedRow = [self.table rectOfRow:selectedRow];
LocationEditViewController *popupController = [[[LocationEditViewController alloc] init] autorelease];
popupController.title = @"Edit Location";
Location *locationToEdit = [self.locationController.selectedObjects objectAtIndex:0];
NSManagedObjectContext *tempContext = [[[NSManagedObjectContext alloc] init] autorelease];
tempContext.parentContext = self.document.managedObjectContext;
Location *tempLocation = (Location *)[tempContext fetchObjectEqualTo:locationToEdit]; // Custom fetch helper method
popupController.managedObjectContext = tempContext;
popupController.representedObject = tempLocation;
[popupController.popover showRelativeToRect:rectOfSelectedRow ofView:sender preferredEdge:NSMaxXEdge];
}
}
这是我想要解释的问题:
弹出窗口中的文本字段通过以下方式连接到弹出窗口的representedObject:笔尖装订。这些与新对象(addLocation:)完美配合。
如果位置是现有对象 (tableViewDoubleClick:),则绑定可以很好地使用位置的属性预先填充字段。但是,更改字段中的文本根本不会改变位置的属性。单击弹出窗口中的“保存”按钮时,我尝试在保存临时上下文之前记录位置的属性。如果它是一个现有对象,则我在字段中输入的任何内容都不会反映在位置的属性中 - 就好像绑定仅进行单向通信一样。
我的解决方法:我发现,如果我跳过绑定并在保存之前将位置的属性手动设置为文本字段中的值,则更改确实会生效。
- (IBAction)popoverSave:(id)sender
{
// These two methods always work. But if I remove these and use bindings instead, it only works for NEW Locations.
[(Location *)self.representedObject setLabel:self.labelField.stringValue];
[(Location *)self.representedObject setLocation:self.locationField.stringValue];
NSLog(@"representedObject = %@", self.representedObject);
NSError *error = nil;
[self.managedObjectContext save:&error];
[self.popover close];
}
我真的很想知道为什么会这样,以防万一我真的做错了什么。
谢谢!
Background:
In my app, I'm specifically targeting Mac OS X Lion. This issue involves Core Data, an NSPopover
and a child NSManagedObjectContext
(created by using the new parentContext property of NSManagedObjectContext
).
I have a table of NSManagedObjects
of class "Location". There's an Add button that calls addLocation: and if a table row is double-clicked, I call tableViewDoubleClick:.
For either case, what I do is create a new NSManagedObjectContext
and set its parent context to that of the document's context. I then either create a new Location in that context or fetch the Location to be edited from the temporary context. I set the popover's representedObject property to the location in question. If I cancel the popover, nothing is saved. If the user clicks a Save button in the popover, I just call save: on the temporary context and the changes get pushed to the main context.
addLocation:
- (IBAction)addLocation:(id)sender
{
LocationEditViewController *popupController = [[[LocationEditViewController alloc] init] autorelease];
popupController.title = @"Add New Location";
NSManagedObjectContext *tempContext = [[[NSManagedObjectContext alloc] init] autorelease];
tempContext.parentContext = self.document.managedObjectContext;
Location *tempLocation = [NSEntityDescription insertNewObjectForEntityForName:@"Location" inManagedObjectContext:tempContext];
popupController.representedObject = tempLocation;
popupController.managedObjectContext = tempContext;
[popupController.popover showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMaxYEdge];
}
tableViewDoubleClick:
- (void)tableViewDoubleClick:(id)sender
{
NSInteger selectedRow = [self.table selectedRow];
if (selectedRow != -1)
{
NSRect rectOfSelectedRow = [self.table rectOfRow:selectedRow];
LocationEditViewController *popupController = [[[LocationEditViewController alloc] init] autorelease];
popupController.title = @"Edit Location";
Location *locationToEdit = [self.locationController.selectedObjects objectAtIndex:0];
NSManagedObjectContext *tempContext = [[[NSManagedObjectContext alloc] init] autorelease];
tempContext.parentContext = self.document.managedObjectContext;
Location *tempLocation = (Location *)[tempContext fetchObjectEqualTo:locationToEdit]; // Custom fetch helper method
popupController.managedObjectContext = tempContext;
popupController.representedObject = tempLocation;
[popupController.popover showRelativeToRect:rectOfSelectedRow ofView:sender preferredEdge:NSMaxXEdge];
}
}
Here's the problem that I'd like an explanation for:
The text fields in the popover are connected to the popover's representedObject via bindings in the nib. These work perfectly with a new object (addLocation:).
If the Location is an existing object (tableViewDoubleClick:), the bindings work well enough to pre-populate the fields with the Location's properties. However, changing the text in the fields does not alter the Location's properties at all. When the Save button in the popup is clicked, I tried logging the Location's properties before saving the temporary context. If it's an existing object, whatever I type into the fields isn't being reflected in the Location's properties - as if the bindings are only communicating one-way.
My workaround: I found that if I skip the bindings and just manually set the Location's properties to the values in the text fields before the save, that the changes do take effect.
- (IBAction)popoverSave:(id)sender
{
// These two methods always work. But if I remove these and use bindings instead, it only works for NEW Locations.
[(Location *)self.representedObject setLabel:self.labelField.stringValue];
[(Location *)self.representedObject setLocation:self.locationField.stringValue];
NSLog(@"representedObject = %@", self.representedObject);
NSError *error = nil;
[self.managedObjectContext save:&error];
[self.popover close];
}
I'd really like to know why this is the case, just in case I'm actually doing something wrong.
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我认为这很可能是这些台词中的演员阵容:
……使它们发挥作用。如果是这样,那么您可能在绑定中的某个位置设置了 NSObject 或 NSManagedObject 作为类,而不是
Location
类。当绑定向通用类发送Location
类特定消息(例如设置具有特定名称的属性)时,通用类会默默地忽略该消息。顺便说一句,我会警告不要使用多个上下文而不是使用撤消 API。我看到很多人都这样陷入麻烦。回滚单个上下文比管理多个上下文更容易。
I think it's likely that it is the cast in these lines:
… that makes them work. If so, then you probably have a NSObject or NSManagedObject set somewhere in the bindings as the class instead of the
Location
class. When the binding sends aLocation
class specific message e.g. set an attribute with a specific name, to the generic class, the generic class silently ignores the message.BTW, I would caution against using multiple context instead of using the undo API. I see a lot of people get in trouble that way. It's easier to roll back a single context than it is to manage multiple context.