不能同时添加子对象和父对象

Posted

技术标签:

【中文标题】不能同时添加子对象和父对象【英文标题】:Can not add a child and parent object at same time 【发布时间】:2015-11-16 12:18:09 【问题描述】:

我有一个由 CoreData 支持的应用程序,它代表某种文件夹层次结构。

所以一个文件夹可以有一个子文件夹。

因此NSManagedObject关系

服务器端目前仅支持两级层次结构。 主文件夹 -> 子文件夹。

但为了为未来做好准备,CoreData-Model 旨在支持无穷无尽的关卡。

所以关系是

parent <------>> children

文件夹还有一个属性name

因此,NSPredicate 的格式为 "parent != nil"NSFetchedResultsController 的格式为 parent.name sectionNameKeyPath

现在 FRC 将不包含空白部分(在此上下文中:没有子文件夹的文件夹)。我创建了一个默认子文件夹(这个文件夹在内部使用......所以我不会搞乱用户层次结构)。

如果用户创建一个新的主文件夹,我再次执行以下操作:

Folder * mainFolder = [self createNewFolderWithName:userSetName];
Folder * systemChildSubfolder = [self createNewFolderWithName:SYSTEM_SUBFOLDER_NAME];

[mainFolder addChildFolderObject:systemChildSubfolder];

[self saveContext];

这很好,但是当 FRC 的委托方法启动时,我会得到这个错误:

CoreData: error: Serious application error.  An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:.  attempt to insert row 0 into section 3, but there are only 0 rows in section 3 after the update with userInfo (null)

看来,FRC 偶然发现了新部分和插入的行。 但我还没有找到解决这个问题的方法。即使我在创建并保存了 mainFolder 之后创建并添加了systemChildSubfolder……我仍然会收到该错误。 FRC 似乎仍然返回旧的节数并尝试在错误的节中添加新行?

所以任何人都可以向我解释如何让 FRC 像我期望的那样工作: 先加节再加行?

FRC 代表:

#pragma mark - NSFetchedResultsControllerDelegate

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller 

        [[self folderTableView] beginUpdates];
    


- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type


    switch (type) 
        case NSFetchedResultsChangeInsert:
            [[self folderTableView] insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
                            withRowAnimation:UITableViewRowAnimationFade];
            return;

        case NSFetchedResultsChangeDelete:
            [[self folderTableView] deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
                            withRowAnimation:UITableViewRowAnimationFade];
            return;
        default:
            return;
    


- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
       atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
      newIndexPath:(NSIndexPath *)newIndexPath


    switch(type) 
        case NSFetchedResultsChangeInsert:

            [[self folderTableView] insertRowsAtIndexPaths:@[newIndexPath]
                                    withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeDelete:
                [[self folderTableView] deleteRowsAtIndexPaths:@[indexPath]
                                        withRowAnimation:UITableViewRowAnimationFade];

            break;
        case NSFetchedResultsChangeUpdate:
        

                UITableViewCell * tableViewCell = [[self tableView] dequeueReusableCellWithIdentifier:TableViewCellIdentifier forIndexPath:indexPath];
                Folder * folder = [[self fetchedResultsController] objectAtIndexPath:indexPath];
                [self configureTableViewCell:tableViewCell withFolder:folder];

        
            break;
        case NSFetchedResultsChangeMove:

            [[self folderTableView] insertRowsAtIndexPaths:@[newIndexPath]
                                          withRowAnimation:UITableViewRowAnimationFade];

            [[self folderTableView] deleteRowsAtIndexPaths:@[indexPath]
                                          withRowAnimation:UITableViewRowAnimationFade];


            break;
    



- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller

   [[self folderTableView] endUpdates];

简短更新:

我怀疑 FRC 不会为新父级更新,尽管它有一个子文件夹。

但是新子节点的更新具有正确的 IndexPath(如果它的父节点在 FRC 中),但它与 FRC 中存储的内容不匹配。

【问题讨论】:

如果你想支持任意深度并允许用户向下钻取,为什么不是"parent == nil"。您当前的界面不会显示层次结构,它将是一个非常大的分段列表......它还会避免您为管理创建的文件夹 行是子文件夹,它们确实有父文件夹。父文件夹将在这些部分中表示。一切正常且正确,问题出在 FRC 的某个地方,它是委托 但是当您添加第三个深度的文件夹时,它们也将显示为行,因此您的第 2 级行也将成为具有更多行的部分 - 这真的是您/用户想要的吗? 另外,您没有显示委托实现,或使用 FRC 的表委托,或列出崩溃前收到的更改 你说得对……但目前我们只有 2 个级别,因此可以有父级!=nil 【参考方案1】:

两个层次

正如 cmets 中所指出的,您的设置对于两个以上的级别是不可行的。因为你只想处理父母和孩子,我推荐以下简单的解决方案:

创建两个实体,MainFolderSubFolder。如果你愿意,你可以有一个Folder 的抽象父实体,但这不是绝对必要的。

MainFolder (subFolders) <------>> (mainFolder) SubFolder

获取SubFolders 并使用mainFolder 作为`sectionNameKeyPath。

就是这样,再简单不过了。


无限关卡

如果您想实现一个可能无限的层次结构,您或许应该在完成此设置后提出一个新问题。

提示:您将只有一种实体Folder,它也将具有属性level。创建文件夹并设置parent 时,您将检查level 并将自己的level 设置为父级加一。层次结构上下的关系将是自反的,即引用同一个Folder 实体。

在 FRC 中,您将在没有 sectionNameKeyPath 的情况下获取父母(通过基于级别的谓词),使用 objectAtIndexPath 作为部分,并根据相应表格视图中的 folder.children 填充部分下方的行datasource 方法(numberOfRowsInSectioncellForRowAtIndexPath)。

【讨论】:

我的 CoreData-Model 是为无限关卡而设计的。但是 TableView 目前无法或更具体地对待 1 级以下的任何文件夹均等。同样,这不是问题!再次解释一下:问题在于 FRC 不包含任何级别 1 的新文件夹(在该部分中表示),即使它带有至少一个子文件夹。这是标准的 FRC 行为,没有任何对象的部分将被忽略,但这个新部分带有一个对象。目前看来,FRC 不处理(插入)新部分,而是尝试插入行对象。 我明白了。我的答案包含解决方案。 我也尝试过这种方法……但它还有其他一些缺点。昨天我做了一些广泛的调试,很奇怪,FRC 已更新,在添加新的父级和默认子级后,它具有该部分。但是 tableview 控制器没有更新……更奇怪的是:在创建新文件夹并出现在 FRC 之后,我做了一个reloadSections:withRowAnimation:,但它绝对没有效果。事件更多,我做了一个reloadData(如果使用 FRC,应该完全避免)......但即使是reloadData 也不会对 tableView 进行任何刷新。

以上是关于不能同时添加子对象和父对象的主要内容,如果未能解决你的问题,请参考以下文章

小程序父组件向子组件传值

如何在两个类中双重引用子类和父类

Vue动态添加和删除组件的实现,子组件和父组件的传值实例演示

试图向视图添加子视图,但子视图不显示

关于子类对象的构造函数和父类构造函数的执行顺序

C++ 继承:子类实例同时使用子构造函数和父构造函数