不能同时添加子对象和父对象
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 中所指出的,您的设置对于两个以上的级别是不可行的。因为你只想处理父母和孩子,我推荐以下简单的解决方案:
创建两个实体,MainFolder
和 SubFolder
。如果你愿意,你可以有一个Folder
的抽象父实体,但这不是绝对必要的。
MainFolder (subFolders) <------>> (mainFolder) SubFolder
获取SubFolders
并使用mainFolder
作为`sectionNameKeyPath。
就是这样,再简单不过了。
无限关卡
如果您想实现一个可能无限的层次结构,您或许应该在完成此设置后提出一个新问题。
提示:您将只有一种实体Folder
,它也将具有属性level
。创建文件夹并设置parent
时,您将检查level
并将自己的level
设置为父级加一。层次结构上下的关系将是自反的,即引用同一个Folder
实体。
在 FRC 中,您将在没有 sectionNameKeyPath
的情况下获取父母(通过基于级别的谓词),使用 objectAtIndexPath
作为部分,并根据相应表格视图中的 folder.children
填充部分下方的行datasource
方法(numberOfRowsInSection
和 cellForRowAtIndexPath
)。
【讨论】:
我的 CoreData-Model 是为无限关卡而设计的。但是 TableView 目前无法或更具体地对待 1 级以下的任何文件夹均等。同样,这不是问题!再次解释一下:问题在于 FRC 不包含任何级别 1 的新文件夹(在该部分中表示),即使它带有至少一个子文件夹。这是标准的 FRC 行为,没有任何对象的部分将被忽略,但这个新部分带有一个对象。目前看来,FRC 不处理(插入)新部分,而是尝试插入行对象。 我明白了。我的答案包含解决方案。 我也尝试过这种方法……但它还有其他一些缺点。昨天我做了一些广泛的调试,很奇怪,FRC 已更新,在添加新的父级和默认子级后,它具有该部分。但是 tableview 控制器没有更新……更奇怪的是:在创建新文件夹并出现在 FRC 之后,我做了一个reloadSections:withRowAnimation:
,但它绝对没有效果。事件更多,我做了一个reloadData
(如果使用 FRC,应该完全避免)......但即使是reloadData
也不会对 tableView 进行任何刷新。以上是关于不能同时添加子对象和父对象的主要内容,如果未能解决你的问题,请参考以下文章