核心数据 - 关系意外为零
我正在努力应对核心数据中的一些奇怪行为。我有一个相当标准的设置,使用 CoreDataBook 示例:我有一个 RootView,它使用 NSFetchedResultsController 来显示项目列表。项目具有一些属性以及与其他实体的关系。我有一个 DetailView,用于创建新项目,以及编辑现有项目(以模态方式呈现)。在 DetailView: viewDidLoad 中,我创建一个新的 ManagedObjectContext,我想在其中进行所有更改...如果用户按“保存”,我会保存此上下文并将更改合并回来;否则,如果用户按取消,所有这些更改都会消失。
“添加新项目”部分工作正常,但是当我选择行来调出与现有项目相同的 DetailView 时,其中一个关系(在调试器的 RootView 中显示良好)在出现时突然变为 nil在详细信息视图中。下面是在 UITableView 的 didSelectRowAtIndexPath 中显示 DetailView 的代码:
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];
DetailView 控制器中没有任何异常情况会导致这种情况。事实上,它甚至没有机会真正造成任何损害......一旦开始,existingItem.title关系就已经为零。 [existingItem 是 DetailView 的保留属性]
知道我应该从哪里开始研究这个吗?过去几个小时这让我发疯。这是 DetailView viewDidLoad 中的一些代码,但即使在调用之前,关系也为零:
// Create a new managed object context
NSManagedObjectContext *addingContext = [[NSManagedObjectContext alloc] init];
self.addEditContext = addingContext;
[addingContext release];
[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]];
}
标题关系是通过从某个列表中选择来设置的:
self.existingItem.title = selectedTitle;
在 save: 方法中,我保存 addEditContext 并将更改与 appdelegate 上下文合并:
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;
在 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];
}
所以save: stuff 对于新的 Item 工作正常,但是当该 Item 设置为executionItem 并再次加载时, self.existingItem.title 为零。从它在视图控制器中呈现的那一刻起,它就为零(即使在呈现之前它不是零)。因此,在主上下文中,它加载 Item 并且其标题关系良好,但当标题出现在presentModalViewController:navController 中时,标题突然消失。
真的很奇怪。如果有人能对此有所了解,我们将不胜感激。
更新:另一件事要提到的是,标题肯定是持久化在perpetualStore中的。每次我关闭并重新加载应用程序时,RootView 都会显示正在设置的标题关系。一旦我选择了该行,关系就变为零。
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 release];
[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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您的设计过于复杂和多余。我认为这种关系显示为零,因为您为
self.existingItem
属性分配了错误的类。首先,没有理由仅为同一前台线程上的另一个视图创建另一个上下文。只需将现有上下文传递到下一个视图,就可以省去因无功能原因而合并到上下文的麻烦。
其次,这个块:
... 完全没有意义,因为您将 self.existingItem 设置为自身。您不妨这样写:
... 因为一旦对象被保存,对象 ID 就固定在持久存储中。如果对象尚未保存,则 ID 是临时的,其他上下文无论如何都无法找到它。
问题最可能的原因是
existingItem
属性的定义。如果您将其定义为id
或NSManagedObject
那么它不会响应title
选择器并返回 nil。它会出错,但您已将其转换为Item
,因此编译器认为它将响应Item
对象将响应的所有消息。除非您知道将迫使一个对象模仿另一个对象,否则不要使用强制转换。否则,您只是在创造机会向编译器隐藏错误。
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:
... is completely pointless because you are setting
self.existingItem
to itself. You might as well have written:... 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 asid
orNSManagedObject
then it won't respond to the thetitle
selector and will return nil. It would error out but you've cast it as aItem
so the compiler thinks it will respond to all the messages that anItem
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.