Modifying a managed object fetched from an NSArrayController
OK, so I'm having a go at building a Mac OS X app using Core Data.
The basic layout is that there is a main window containing a NSTableView
, which displays a brief description of all objects managed by the app; the entities are simple, containing a few fields like a title, date, and notes. The main window provides commands to add, delete, and modify entries. When adding or modifying an entry, the app disp开发者_Python百科lays a new window that has all the properties of that object available to edit.
The editor window lives in its own .nib, is managed by a subclass of NSWindowController
, and is loaded via a [[SUBCLASS alloc] initWithWindowNibName:]
call. The editable fields in this window are bound to an NSObjectController
, which will manage a single entry out of the list. This controller is not bound to anything out of the nib; when this controller gets loaded, its managedObjectContext
and content
values are set to the main object context and the entity being edited, respectively.
So, adding an object works great, and works like this:
NSEntityDescription *entityDesc = [[self.managedObjectModel entitiesByName] objectForKey: @"LogEntryEntity"];
LogEntryEntity *entry = (LogEntryEntity *) [[NSManagedObject alloc] initWithEntity: entityDesc
insertIntoManagedObjectContext: self.managedObjectContext];
LogEditorController *editor = [[LogEditorController alloc] initWithWindowNibName: @"LogEditorWindow"
logEntry: entry];
entry.date = [NSDate dateOneHourAgoTo30Minutes];
[editor setSaveHandler: ^(LogEditorController *c)
{
NSError *error = nil;
if (![self.managedObjectContext save: &error])
NSLog(@"Failed to save object: %@", error);
[self.logTableView reloadData];
}];
[entry release];
[editor loadWindow];
[editor showWindow: self];
Delete works, too:
NSIndexSet *selectedIndexes = [self.logTableView selectedRowIndexes];
if ([selectedIndexes count] == 0)
return;
[self.logArrayController removeObjectsAtArrangedObjectIndexes: selectedIndexes];
if (![self.managedObjectContext save: &error])
NSLog(@"error saving: %@", error);
But when I go to edit a selected entry:
NSIndexSet *selectedIndexes = [self.logTableView selectedRowIndexes];
if ([selectedIndexes count] != 1)
return;
LogEntryEntity *entry = (LogEntryEntity *) [[self.logArrayController arrangedObjects] objectAtIndex: [selectedIndexes firstIndex]];
LogEditorController *editor = [[LogEditorController alloc] initWithWindowNibName: @"LogEditorWindow"
logEntry: entry];
[editor setSaveHandler: ^(LogEditorController *c)
{
NSError *error = nil;
if (![self.managedObjectContext save: &error])
NSLog(@"error saving: %@", error);
}];
[editor loadWindow];
[editor showWindow: self];
What happens here, though, is that when the window appears, the fields are filled out with the contents of the correct entry. But, immediately after, all the fields get set to the values of some other entry (perhaps not coincidentally, it gets set to the one with the smallest objectID of all objects), and I can confirm when the window closes that the content
value of the NSObjectController
has changed to that different entity. When I first set content
, I've confirmed that it is the one I want to edit.
What's going on here? I mean, it's clear that I'm doing something wrong, but I can't figure out what.
I don't see from this code how the editor
window could switch LogEntryEntity
objects unless you have some other code or have bound the editor window incorrectly. You are passing a specific object and not an array of objects so how does the editor
window even find the other objects to incorrectly display?
I would suggest removing the cast from this line:
LogEntryEntity *entry = (LogEntryEntity *) [[self.logArrayController arrangedObjects] objectAtIndex: [selectedIndexes firstIndex]];
... because if for some reason the object returned from the array is not actual a LogEntryEntity
object, you will never know. It could be another managedObject or something else entirely. Cast in Objective-C are very powerful and the compiler, of course, trust them implicitly.
精彩评论