I am struggling with some bizarre behavior in Core Data. I have a fairly standard set-up, using the CoreDataBook example: I have a RootView which is using a NSFetchedResultsController to display list of Items. Item has a few attributes and relationships to other entities. I have a DetailView which I use to create a new Item, as well as edit an existing Item, which I present modally. In DetailView: viewDidLoad, I create a new managedObjectContext in which I want to make all the changes ... if the user presses Save, I save this context and merge the changes back; otherwise, if the user presses cancel, all those changes just disappear.
The "add new Item" part of this works fine, but when I select the row to bring up the same DetailView which an existing Item, one of the relationships (which shows fine in the RootView in the debugger) suddenly becomes nil when it's presented in the DetailView. Here's the code to display the DetailView in UITableView's didSelectRowAtIndexPath:
Item *managedObject = (Item *)[self.fetchedResultsController objectAtIndexPath:indexPath];
DetailView *childController = [[DetailView alloc] initWithNibName:@"DetailView" bundle:nil];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:childController];
childController.existingItem = managedObject;
// ** Item's relationship to Title is not nil at this point
[self presentModalViewController:navController animated:YES];
// ** Item's relationship Title is now nil
[childController release];
[navController release];
There's nothing extra-ordinary in the DetailView controller that would cause this. In fact, it doesn't even have a chance to really to any damage ... as soon as it starts, the existingItem.title relationship is already nil. [existingItem is a retained property of DetailView]
Any idea where I should start looking into this? It's been driving me crazy for the past few hours. Here's some code in DetailView viewDidLoad, but the relationship is nil even before it's called:
// Create a new managed object context
NSManagedObjectContext *addingContext = [[NSManagedObjectContext alloc] init];
self.addEditContext = addingContext;
[addingContext r开发者_如何学Pythonelease];
[self.addEditContext setPersistentStoreCoordinator:[[appDelegate managedObjectContext] persistentStoreCoordinator]];
if (!self.existingItem) {
self.existingItem = [NSEntityDescription insertNewObjectForEntityForName:@"Item" inManagedObjectContext: self.addEditContext];
}else{
self.existingItem = (Item *)[self.addEditContext objectWithID:[self.existingItem objectID]];
}
The title relationship is set by selecting from some list:
self.existingItem.title = selectedTitle;
In the save: method, I save the addEditContext and merge changes with the appdelegate context:
NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];
[dnc addObserver:self selector:@selector(addControllerContextDidSave:) name:NSManagedObjectContextDidSaveNotification object: self.addEditContext];
// Save the context.
NSError *error = nil;
if (![self.addEditContext save:&error]){
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
}
[dnc removeObserver:self name:NSManagedObjectContextDidSaveNotification object:self.addEditContext];
self.addEditContext = nil;
In addControllerContextDidSave:
- (void)addControllerContextDidSave:(NSNotification*)saveNotification {
id appDelegate = [[UIApplication sharedApplication] delegate];
// Merging changes causes the fetched results controller to update its results
[[appDelegate managedObjectContext] mergeChangesFromContextDidSaveNotification:saveNotification];
}
So the save: stuff works fine for a new Item, but when that Item is set to existingItem and loaded again, the self.existingItem.title is nil. And it's nil from the point where it's presented in the view controller (even though it's not nil before it's presented). So in the main context, it loads the Item and it's title relationship fine, but then title suddenly disappears when it's presented in presentModalViewController:navController.
Really strange. If anyone can shed some light on this, it would be really appreciated.
Update: another thing to mention is that the title is definitely persisted in the persistentStore. Every time I close and reload the app, the RootView shows the title relationship being set. As soon as I select the row, the relationship becomes nil.
Your design is all needlessly complex and redundant. I think the relationship shows as nil because you've got the wrong class assigned to your self.existingItem
property.
Firstly, there is no reason to create another context just for another view on the same foreground thread. Just pass the existing context to the next view and save yourself the headache of merging to context for no functional reason.
Secondly, this block:
}else{
self.existingItem = (Item *)[self.addEditContext objectWithID:[self.existingItem objectID]];
}
... is completely pointless because you are setting self.existingItem
to itself. You might as well have written:
self.existingItem = self.existingItem;
... because object ids are fixed in the persistent store once an object as been saved. If the object hasn't been saved then the ID is temporary and the other context won't be able to find it anyway.
The most likely cause of the problem is with the definition of the existingItem
property. If you defined it as id
or NSManagedObject
then it won't respond to the the title
selector and will return nil. It would error out but you've cast it as a Item
so the compiler thinks it will respond to all the messages that an Item
object would.
Don't use cast unless you know you will be forcing an object to mimic another. Otherwise, you are just creating opportunities to hide errors from the compiler.
精彩评论