即使没有保存上下文,NSFetchedResultsController 委托也会触发

Posted

技术标签:

【中文标题】即使没有保存上下文,NSFetchedResultsController 委托也会触发【英文标题】:NSFetchedResultsController delegate fired even when context not saved 【发布时间】:2012-08-29 19:03:35 【问题描述】:

我有一个覆盖 validateForInsert 和 validateForUpdate 的 NSManagedObject 实体。

根据我编写的一些逻辑,当对象一致性出现问题时,此方法会正确返回 NO。

该应用程序是一个由 NSFetchedResultsController 支持的经典 uitableview,带有一个详细视图控制器。

当我添加一个新实体时,详细视图控制器被实例化为一个 nil objectID,并被推送到导航堆栈中。 当我弹出细节控制器时,[context save:&error] 被调用,并且通过放置一些断点,我发现当我添加一个新实体时,获取的委托方法被触发一次,即使 validateForInsert 返回 NO,并且 includePendingChanges 设置为也没有。

当我再次尝试弹出详细控制器时,当然会再次调用保存,验证方法也是如此,但是这次 NSFetchedResultsControllerDelegate 没有被触发。

我想知道这是否是正常行为,或者我的模型中是否缺少某些内容。

[更新]

这是委托方法中断点后的堆栈跟踪:

#0  0x0003e5ba in -[MyTableViewController controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:] at ......
#1  0x011512f9 in -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] ()
#2  0x00b46a29 in __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_0 ()
#3  0x01759855 in ___CFXNotificationPost_block_invoke_0 ()
#4  0x01759778 in _CFXNotificationPost ()
#5  0x00a8b19a in -[NSNotificationCenter postNotificationName:object:userInfo:] ()
#6  0x0106a673 in -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] ()
#7  0x01101f5e in -[NSManagedObjectContext(_NSInternalChangeProcessing) _createAndPostChangeNotification:withDeletions:withUpdates:withRefreshes:] ()
#8  0x01065ad3 in -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] ()
#9  0x0106916b in -[NSManagedObjectContext save:] ()
#10 0x00004490 in -[MyAppDelegate saveContext:]

可以看出,即使在上下文中调用了 save,也没有调用 validate,因此这会触发获取的控制器委托。

[更新 2]

特别是,不知道是否相关,这导致controllerDidChangeObject中出现异常(并非总是如此):

CoreData:错误:严重的应用程序错误。捕获到异常 在调用期间从 NSFetchedResultsController 的委托 -controllerDidChangeContent:。无效更新:第 0 节中的行数无效。现有节中包含的行数 更新后(4)必须等于包含的行数 更新前的那个部分(3),加减行数 从该部分插入或删除(0 插入,0 删除)和加上 或减去移入或移出该部分的行数(0 移动 入,0 移出)。与 userInfo (null)

而实际上origin表有3行,只是因为context在context中创建object时绕过了validateFor方法,好像插入了一行,所以行数不一致。

控制器委托非常简单和标准:

-(void)controllerWillChangeContent:(NSFetchedResultsController*)controller 
   [self.tableView beginUpdates];


- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath 
   // for testing purpose I am not doing any modification
   return;


-(void)controllerDidChangeContent:(NSFetchedResultsController*)controller 
   [self.tableView endUpdates];

还是不知道是谁在更新表格视图

【问题讨论】:

调用什么委托方法? fetched results controller 提供的内容是否真的改变了?也许您可以发布堆栈跟踪以显示对您的委托的调用? 调用的方法就是这个控制器:didChangeObject:atIndexPath:forChangeType:newIndexPath: 【参考方案1】:

我认为这是正常行为;获取的结果控制器在显示更改之前不需要保存。如果您不希望在详细视图中所做的更改在保存之前出现在主视图中,则必须为详细视图创建一个单独的 NSManagedObject 上下文。这样,只有保存到持久存储的对象才会返回到获取的结果控制器。

【讨论】:

是的,但正如文档中所述:“NSFetchedResultsController 的一个实例使用此协议中的方法来通知其委托,控制器的获取结果已因添加、删除、移动或更新操作而发生更改。 ",这个问题是否应该通过设置 includePendingChanges 为 NO 来解决? 不;更新立即发生在您的控制器所在的托管对象上下文中。获取请求中的includePendingChanges 标志实际上不是 NSFetchedResultsController 可以支持的。 现在已经很清楚了,你能看看剩下的问题吗?我添加了几行。 好的,忘记这一点,按照你的回答,很明显我必须自己添加一行以匹配表格行数。 如果您不希望这些更改出现在主视图中,我强烈建议您为详细视图创建另一个 NSManagedObject 上下文。

以上是关于即使没有保存上下文,NSFetchedResultsController 委托也会触发的主要内容,如果未能解决你的问题,请参考以下文章

不保存上下文的 CoreData 保存

为啥我在 SwiftUI 中重新打开应用程序时没有保存 CoreData?

即使删除后,已删除的 NSManagedObject 仍保留在内存中

即使在使用 DelegatingSecurityContextAsyncTaskExecutor 之后,Spring 上下文也没有得到更新

即使在“保存”调用之后,Django 对象也没有保存

是否可以看到添加的实体从一个未保存EF4背景?