【问题标题】:Core Data - relationship goes nil unexpectedly核心数据 - 关系意外地为零
【发布时间】:2011-07-31 07:23:15
【问题描述】:

我正在为 Core Data 中的一些奇怪行为而苦苦挣扎。我有一个相当标准的设置,使用 CoreDataBook 示例:我有一个 RootView,它使用 NSFetchedResultsController 来显示项目列表。项目有一些属性和与其他实体的关系。我有一个 DetailView,我用它来创建一个新项目,以及编辑一个现有的项目,我以模态方式呈现。在 DetailView: viewDidLoad 中,我创建了一个新的 managedObjectContext,我想在其中进行所有更改……如果用户按下保存,我保存此上下文并将更改合并回来;否则,如果用户按下取消,所有这些更改都会消失。

“添加新项目”部分工作正常,但是当我选择行以显示与现有项目相同的 DetailView 时,其中一个关系(在调试器的 RootView 中显示良好)突然变为 nil当它出现在 DetailView 中时。这是在 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 对新项目工作正常,但是当该项目设置为 existingItem 并再次加载时,self.existingItem.title 为 nil。从它在视图控制器中呈现的角度来看,它是 nil (即使它在呈现之前不是 nil )。所以在主上下文中,它加载了Item并且它的标题关系很好,但是当它出现在presentModalViewController:navController中时,标题突然消失了。

真的很奇怪。如果有人能对此有所了解,将不胜感激。

更新:还有一点要提的是,标题肯定是持久化的。每次我关闭并重新加载应用程序时,RootView 都会显示正在设置的标题关系。一旦我选择了行,关系就变为零了。

【问题讨论】:

    标签: core-data nsmanagedobjectcontext


    【解决方案1】:

    您的设计都是不必要的复杂和多余的。我认为这种关系显示为 nil,因为您为 self.existingItem 属性分配了错误的类。

    首先,没有理由只为同一个前台线程上的另一个视图创建另一个上下文。只需将现有的上下文传递给下一个视图,就可以省去无缘无故合并到上下文的麻烦。

    其次,这个区块:

    }else{
        self.existingItem = (Item *)[self.addEditContext objectWithID:[self.existingItem  objectID]];
    }
    

    ... 完全没有意义,因为您将 self.existingItem 设置为自身。你不妨这样写:

    self.existingItem = self.existingItem;
    

    ... 因为一旦对象被保存,对象 ID 就固定在持久存储中。如果对象尚未保存,则 ID 是临时的,并且其他上下文无论如何都无法找到它。

    问题的最可能原因是existingItem 属性的定义。如果您将其定义为idNSManagedObject,那么它将不会响应title 选择器并且将返回nil。它会出错,但您已将其转换为 Item,因此编译器认为它将响应 Item 对象会响应的所有消息。

    除非你知道你会强迫一个对象模仿另一个对象,否则不要使用强制转换。否则,您只是在创造机会向编译器隐藏错误。

    【讨论】:

    • 感谢您的 cmets。 existingItem 绝对设置为 @property(retain,nonatomic) Item *existingItem 所以不是这样。我创建另一个上下文的原因是我想让用户对现有项目进行大量更改(或添加新项目时),但页面上有一个取消按钮可以回滚所有内容,以防用户决定。我觉得最简单的方法是创建一个单独的上下文,这样更改就不会影响主线程的上下文。有没有更好的方法来做到这一点?
    • 我还尝试使用 NSUndoManager 来跟踪和回滚更改,而不是使用多个上下文。但它也有同样的问题。 'title' 关系在 RootView 的 didSelectRowAtIndexPath 中很好,直到它调用 [self presentModalViewController:navController animated:YES] 然后它从 RootView 和 DetailView 中消失。现在使用 UndoManager,当取消编辑时,标题会在视图上恢复,但它仍然没有显示在 DetailView 中(即使所有内容都在同一上下文中)。这是否表明我可能做错了什么?
    • 另外,我添加了另一个关系(一对一),它工作正常并显示在 DetailView 中。标题关系是多对一的(从Items到title...所以一个title可以关联多个item,但是每个Item都有一个title)。
    • 我最终使用了临时变量,并将新对象插入到上下文中,如果取消则删除它们。它实际上比我在多个上下文中尝试做的要复杂得多,但它至少可以可靠地工作。从概念上讲,我不明白为什么使用另一个上下文是有问题的……难道不鼓励创建单独的上下文来处理不同的“便签本”,尤其是在处理多个线程时?那么,如果它仍然在主线程上,为什么会有任何不同呢?
    • 您可以使用不同的上下文,但不能真正用作“便签本”,尽管我以前见过这种描述。该 API 旨在使用撤消管理来完成您想要的。随着您的模型变得越来越大和越来越复杂,使用多个上下文将变得越来越困难,而撤消将继续扩展。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-18
    相关资源
    最近更新 更多