ios - 在 viewWillDisappear 中将 FetchedResultsController 设置为 nil 后出现错误

标签 ios swift uitableview core-data nsfetchedresultscontroller

在我的 iOS(Swift 3、Xcode 8)Core Data 应用程序中,我有 2 个 View Controller (CategoriesViewController 和 ObjectsViewController)(两者都位于同一个导航 Controller 内)。

每个ViewController都有自己的tableView和自己的fetchResultsController来管理从Core Data请求返回的结果(在CategoriesViewController中获取标题为Category的实体和在ObjectsViewController中获取标题为Object的实体)。

在我的 CategoriesViewController 中,我有这个变量:

var fetchResultsController: NSFetchedResultsController<Category>!

我已将以下代码添加到 CategoriesViewController 以避免打开另一个 View 时出现错误:

override func viewWillDisappear(_ animated: Bool) {
          super.viewWillDisappear(true)

          self.fetchedResultsController.delegate = nil
          self.fetchedResultsController = nil

     }

在 CategoriesViewController 中,我为 fetchedResultsController 添加了以下方法:

func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
      self.tableView.beginUpdates()
 }

 func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
      self.tableView.endUpdates()

 }

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {

          switch type {
          case .update:
               guard let path = indexPath
                    else { return }
               tableView.reloadRows(at: [path], with: .automatic)

          case .delete:
               guard let path = indexPath
                    else { return }
               tableView.deleteRows(at: [path],
                                    with: .automatic)

          case .insert:
               guard let path = newIndexPath
                    else { return }
               tableView.insertRows(at: [path],
                                    with: .automatic)

          case .move:
               guard let _ = indexPath,
                    let _ = newIndexPath
                    else { return }
               // tableView.moveRow(at: fromPath, to: toPath)

               if indexPath != newIndexPath {
                    tableView.deleteRows(at: [indexPath!], with: .none)
                    tableView.insertRows(at: [newIndexPath!], with: .none)
               }



          }

     }

为了获取核心数据对象,我编写了 coreData_fetchAll_Categories()。我已将其放入 CategoriesViewController 的 viewWillAppear 方法中。之后我重新加载 tableView 的数据。

 func coreData_fetchAll_Categories(handleCompleteFetching:@escaping (()->())) {

         let context = CoreDataManager.sharedInstance.viewContext

          let fetchRequest: NSFetchRequest<Category> = Category.fetchRequest()

          var sortDescriptors = [NSSortDescriptor]()

          let indexSortDescriptior = NSSortDescriptor(key: "indexOrder", ascending: true)
          sortDescriptors.append(indexSortDescriptior)

          fetchRequest.sortDescriptors = sortDescriptors

          self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context!, sectionNameKeyPath: nil, cacheName: nil)

          self.coreDataFetchedResultsController.delegate = self

          do {  try self.fetchedResultsController.performFetch()


          } catch  {
               print("performFetch() finished with error")
          }

     }
使用上面的代码,在我从 ObjectsViewController 返回后(其中我还拥有用于对象实体的 fetchedResultsController 的所有方法,并且我还在 viewWillDisappear 中将 fetchedResultsController 设置为 nil),CategoriesViewController 中的 tableView 卡住了。如果我从 CategoriesViewController 的 viewWillDisappear 中删除这两行,一切都会正常,但我需要这些行来避免另一个错误。

self.fetchedResultsController.delegate = nil self.fetchedResultsController = nil

ViewWillAppear 中的代码如下所示:

 override func viewWillAppear(_ animated: Bool) {
          super.viewWillAppear(true)

         self.tableView.register(UINib.init(nibName: “CategoriesTableViewCell", bundle: nil), forCellReuseIdentifier: “categoriesTableViewCell")


          self.tableView.delegate = self
          self.tableView.dataSource = self


self.coreData_fetchAll_Categories {

  DispatchQueue.main.async { // Submit to the main Queue async


                    self.tableView.reloadData()

               }

}

}

出现 CategoryViewController 后,VC 创建新版本的 fetchedResultsController (我已经检查过,它不是 nil)。我还注意到 tableView 不调用 cellForRowAt indexPath: 。看起来很奇怪。 tableView 的委托(delegate)在 viewWillAppear 中设置为 self。

我不明白为什么会发生这个错误,因为我没有收到任何错误。

欢迎对此错误提出任何意见。使用 Swift 3 (XCode 8)。

最佳答案

您必须确保 tableview 和 fetchedResultsController 永远不会不同步。以下是代码中可能发生这种情况的几个地方:

  1. 当您清空 fetchedResultsController 时,您还必须重新加载 tableview,因为现在它应该有零个部分和零个行
  2. coreData_fetchAll_Categories已经在主线程上运行 - 没有理由使用这样的完成处理程序。此外,使用 DispatchQueue.main.async 可能会造成真正的危害,因为当 tableview 和 fetchedResultsController 不同步时,在调用 reloadData 之前有一段时间
  3. 此外(与您的核心数据问题无关)当您调用 super.viewWillDisappearsuper.viewWillAppear 时,您应该传递 animated 参数- 并且并不总是传递 true

希望有帮助

关于ios - 在 viewWillDisappear 中将 FetchedResultsController 设置为 nil 后出现错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46156948/

相关文章:

javascript - 如何在 React Native 中添加多个组件?

iphone - 在iOS中,如何将当前时间,当前时间,分钟和秒保存到单独的整数中?

ios - Objective C 中 iOS 的图像模糊检测

ios - 如何使用 xmpp openfire 发送打字通知

ios - 查看协调者/委托(delegate)消失功能?

ios - 如何在for循环中暂停调度队列?

ios - UIPageViewController 以编程方式进行中间转换?

ios - 通过 tableview 中的按钮进行 segue 执行 2 个 segues 而不是一个

ios - 尝试在 tableView :editActionsForRowAt: 中设置带有图像的按钮

objective-c - 为什么 detailTextLabel 不可见?