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

时间:2017-09-11 13:30:42

标签: ios swift uitableview core-data nsfetchedresultscontroller

在我的iOS(Swift 3,Xcode 8)核心数据应用程序中,我有2个视图控制器(CategoriesViewController和ObjectsViewController)(两者都在同一个导航控制器中)。

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

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

var fetchResultsController: NSFetchedResultsController<Category>!

我已将以下代码添加到CategoriesViewController,以避免在打开另一个视图时出错:

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)
               }



          }

     }

为了获取Core Data对象,我编写了一个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用于Object实体,并且我还在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()

               }

}

}

在出现CategoriesViewController后,VC创建了fetchedResultsController的新版本(我已经检查过,它不是零)。另外我还注意到tableView没有调用cellForRowAt indexPath :.似乎很奇怪。 tableView的委托在viewWillAppear中设置为self。

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

对此bug的任何意见欢迎。在Swift 3(XCode 8)中工作。

1 个答案:

答案 0 :(得分:4)

您必须确保tableview和fetchedResultsController永远不会同步。以下是您的代码中可能出现的一些地方:

  1. 当你取出fetchedResultsController时,你还必须重新加载tableview,因为现在它应该有零节和零行
  2. coreData_fetchAll_Categories已经在主线程上运行了 - 没有理由完成这样的完成处理。此外,使用DispatchQueue.main.async可能会造成真正的危害,因为当tableview和fetchedResultsController不同步时调用reloadData会有一段时间
  3. 另外(与您的核心数据问题无关)当您致电super.viewWillDisappearsuper.viewWillAppear时,您应该传递animated参数 - 而不是总是通过true
  4. 希望有所帮助