【发布时间】:2019-12-21 20:56:39
【问题描述】:
TL;DR
显然,当通过执行 NSBatchUpdateRequest 发生更改时,NSFetchedResultsController 不知道给定 NSManagedObjectContext 上的数据已更改。是否可以强制NSFetchedResultsController 重新加载其数据,以便调用NSFetchedResultsControllerDelegate 上的controller(_:didChange:at:for:newIndexPath:)?
问题
我正在使用获取的结果控制器来获知基础数据的变化,以便在表格视图发生变化时更新它。我正在采用NSFetchedResultsControllerDelegate 协议并实现controller(_:didChange:at:for:newIndexPath:) 方法。一切似乎都在工作,因为当我插入、删除或更新单个项目时调用了 did change 方法。
当我使用NSBatchUpdateRequest 批量更新多个项目时会出现问题。当我这样做时,不会调用委托方法controller(_:didChange:at:for:newIndexPath:)(我在获取结果控制器使用的同一上下文中执行这些更新)。
代码
#1:获取结果控制器初始化:
func createFetchedResultsController() -> NSFetchedResultsController {
let fetchRequest: NSFetchRequest<EAlbum> = NSFetchRequest<EAlbum>(entityName: "EAlbum")
fetchRequest.predicate = NSCompoundPredicate(type: .and, subpredicates: createPredicates())
addSortDescriptors(on: fetchRequest)
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest,
managedObjectContext: AppDelegate.myPersistentContainer.viewContext,
sectionNameKeyPath: nil,
cacheName: nil)
fetchedResultsController.delegate = self
return fetchedResultsController
}
#2:NSFetchedResultsControllerDelegate:
extension FeedTableViewController: NSFetchedResultsControllerDelegate {
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>,
didChange anObject: Any,
at indexPath: IndexPath?,
for type: NSFetchedResultsChangeType,
newIndexPath: IndexPath?) {
switch type {
case .insert:
if let indexPath = newIndexPath {
tableView.insertRows(at: [indexPath], with: .fade)
}
case .delete:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
default:
// TODO
}
}
}
#3:我使用NSBatchUpdateRequest 从AppDelegate.myPersistentContainer.viewContext 更新一堆项目的函数:
func markAllAsRead(using context: NSManagedObjectContext) -> Bool {
let request = NSBatchUpdateRequest(entityName: "EAlbum")
request.predicate = NSPredicate(format: "%K == YES", #keyPath(EAlbum.markUnread))
request.propertiesToUpdate = [#keyPath(EAlbum.markUnread): false]
request.resultType = .statusOnlyResultType
do {
let result = try context.execute(request) as? NSBatchUpdateResult
return result?.result as? Bool ?? false
} catch {
return false
}
}
#4:我只更新 AppDelegate.myPersistentContainer.viewContext 中的一项的函数:
func updateUnreadFlag(albumId: String, markUnread: Bool, using context: NSManagedObjectContext) {
guard let album = try? EAlbum.findAlbum(matching: albumId, in: context) else { return }
album.markUnread = markUnread
do {
try context.save()
} catch {
// Handle error
}
}
调用#4时,会调用委托controller(_:didChange:at:for:newIndexPath:)方法。但是在执行 #3 时,委托方法不会被调用。当通过执行 NSBatchUpdateRequest 发生更改时,NSFetchedResultsController 似乎不知道给定 NSManagedObjectContext 上的数据已更改。
我尝试过的解决方法
由于我的获取结果控制器不知道数据已更改,因此我考虑像这样手动重新加载它:
func reloadFetchedResultsController() {
fetchedResultsController = createFetchedResultsController()
do {
try fetchedResultsController.performFetch()
} catch {
// Handle error
}
tableView.reloadData()
}
所以每次执行 #3 后,我都会调用reloadFetchedResultsController() 重新加载获取的结果控制器和表格视图。但由于某种原因,它似乎加载了旧数据,因为我的表格视图没有任何变化。
问题
答:是否可以让获取结果控制器知道在执行批量更新时数据已更改?
B:是否可以强制我的获取结果控制器重新加载其数据?因为,作为最后的手段,我可以在执行 #3 后强制重新加载获取的结果控制器,最后强制更新表格视图,所以它会使用新的批处理重新加载它的单元格-从获取的结果控制器更新数据。
【问题讨论】:
标签: ios core-data nsfetchedresultscontroller nsbatchupdaterequest