NSFetchedResultsControllerDelegate返回错误的NSFetchedResultsChangeType

时间:2017-04-19 04:38:04

标签: swift core-data xcode8

这是我对委托的实现:

func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    tableView.beginUpdates()
    ItemListTableViewController.logger.log("begin updates")
}

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    tableView.endUpdates()
    ItemListTableViewController.logger.log("end updates")
}

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
    ItemListTableViewController.logger.log(type.rawValue, "\(indexPath) -> \(newIndexPath)")
    switch type {
    case .delete:
        self.tableView.deleteRows(at: [indexPath!], with: .automatic)
    case .insert:
        self.tableView.insertRows(at: [newIndexPath!], with: .automatic)
    case .move:
        self.tableView.moveRow(at: indexPath!, to: newIndexPath!)
    case .update:
        self.tableView.reloadRows(at: [indexPath!], with: .automatic)
    }
}

这是我得到的日志:

ItemListTableViewController >>> "perform insert start" 
ItemListTableViewController >>> "begin updates" 
ItemListTableViewController >>> 1 "nil -> Optional([0, 0])" 
ItemListTableViewController >>> 4 "Optional([0, 1]) -> Optional([0, 2])"
ItemListTableViewController >>> "end updates" 
ItemListTableViewController >>> "perform insert end" 

我尝试通过调用context.insert(item)将新项目插入上下文,并根据第3行日志插入新项目。并且控制器根据第4行移动一些项目。但原始价值的类型&#39; 4&#39; NSFetchedResultsChangeType中的update应该是move,而不是move

我还测试了其他情况,当我需要更新项目时,它给了我一个update类型。

我错了move和{{1}}的含义吗?或者它是一个错误?

我错了。这是一个糟糕的代码。

我正在使用Xcode 8.3.1

2 个答案:

答案 0 :(得分:0)

4是update的枚举值; 3是move的值。您可能会被委派的两个索引路径混淆。 indexPath是更新前的索引(在任何插入或删除之前),newIndexPath是更新后的索引。不幸的是,当涉及到如何使用fetchedResults控制器进行更新时,Apple的文档是错误的。对于更新,您应该使用newIndexPath,因为在插入和删除后处理更新。由于这个原因,您可能会遇到崩溃或不良行为。

此外,您处理移动的方式也不正确。见App crashes after updating CoreData model that is being displayed in a UITableView

答案 1 :(得分:0)

感谢@Joe Rose,我明白这是如何运作的。我不是在处理可能同时插入和移动的任务,所以我没有使用你的解决方案。

我对updatemove的含义错了。所以我再次阅读Apple的文档。它说:

  

使用以下启发式方法报告更改:

     
      
  • 在添加和删除操作中,仅报告添加/删除的对象。

         

    假设受影响对象之后的所有对象也被移动,但不会报告这些移动。

  •   
  • 当对象的已更改属性是获取请求中使用的排序描述符之一时,将报告移动。

         

    在这种情况下,假设更新了对象,但没有向委托发送单独的更新消息。

  •   
  • 当对象的状态发生更改时,将报告更新,但更改的属性不是排序键的一部分。

  •   

因此,报告move时,也会报告update。那时,获取的对象会更新,但tableView不会更新,因此我需要使用indexPath来定位单元格,并newIndexPath来获取正确的对象。

这是我的最终解决方案,现在表现良好:

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
    ItemListTableViewController.logger.log(type.rawValue, "\(indexPath) -> \(newIndexPath)")

    switch type {
    case .insert:
        self.tableView.insertRows(at: [newIndexPath!], with: .automatic)
    case .delete:
        self.tableView.deleteRows(at: [indexPath!], with: .automatic)
    case .move:
        self.tableView.moveRow(at: indexPath!, to: newIndexPath!)
        fallthrough
    case .update:
        self.configure(cell: tableView.cellForRow(at: indexPath!) as! ItemInfoCell, at: newIndexPath!)
    }
}


func configure(cell: ItemInfoCell, at indexPath: IndexPath) {
    let item = fetchController.object(at: indexPath)
    cell.item = item
}

如果有任何问题,请告诉我。