【问题标题】:UICollectionView + FetchResultsController sometimes crashes when i flush CoreData entity当我刷新核心数据实体时,UICollectionView + FetchedResultsController 有时会崩溃
【发布时间】:2017-12-07 10:00:10
【问题描述】:

我对通过 FetchResultsController 链接到 CoreData 实体的 UICollectionView 有疑问。我有一个带有搜索结果的屏幕,所以我需要从 API 获取记录,刷新核心数据实体并添加新记录。

它工作正常,但有时当我点击搜索按钮太快时,UICollection 会损坏并且不再更新。事实上,当我尝试使用不再存在的元素时发生错误。

我添加了一些检查,但它在 100% 的情况下没有帮助。

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


        blockOperations.append(BlockOperation(block: {
            if type == .insert {
                 self.ChatsCollectionView?.insertItems(at: [newIndexPath!])
            }

            if type == .delete {
                if ((indexPath?.row)! <= self.ChatsCollectionView.numberOfItems(inSection: 0) && self.ChatsCollectionView.numberOfItems(inSection: 0) != 0) {
                    self.ChatsCollectionView?.deleteItems(at: [indexPath!])
                }
            }

            if type == .update {
                if ((indexPath?.row)! <= self.ChatsCollectionView.numberOfItems(inSection: 0) && self.ChatsCollectionView.numberOfItems(inSection: 0) != 0) {
                    self.ChatsCollectionView?.reloadItems(at: [indexPath!])
                }
            }

            if type == .move {
                if let indexPath = indexPath {
                    self.ChatsCollectionView.deleteItems(at: [indexPath])
                }

                if let newIndexPath = newIndexPath {
                    self.ChatsCollectionView.insertItems(at: [newIndexPath])
                }
            }

        }))
}

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {

    ChatsCollectionView?.performBatchUpdates({

        for operation in self.blockOperations {
            operation.start()
        }

    }, completion: { (completed) in
        //self.ChatsCollectionView.reloadItems(at: self.ChatsCollectionView.indexPathsForSelectedItems!)
        // I comment this, because it cause crash 

    })
}

所以我现在没有崩溃,但有时我会收到错误,并且 Collection 看起来已损坏(有些行是空的等...):

我不使用 reloadData(),因为当我在 Entity 中刷新数据时它会异步工作并崩溃。

【问题讨论】:

    标签: swift core-data nsfetchedresultscontroller flush


    【解决方案1】:

    使用collectionView 和tableView,如果你搞砸了并且你的数据源确实符合预期(即你插入了一行但行数没有增加),视图将停止进行任何更新,并且可能出现空行。

    从 fetchedResultsController 更新比 Apple 文档中的说明更困难。当移动和插入或移动和删除同时存在时,您共享的代码会导致此类错误。

    indexPath 是应用删除和插入之前的索引; newIndexPath 是应用删除和插入后的索引。

    对于更新,您不在乎在插入和删除之前它在哪里 - 只在之后 - 所以使用 newIndexPath 而不是 indexPath。这将解决当您同时更新和插入(或更新和删除)并且单元格未按预期更新时可能发生的崩溃。

    对于move,代表说它在插入之前从哪里移动以及在插入和删除之后应该插入到哪里。当您进行移动和插入(或移动和删除)时,这可能具有挑战性。您可以通过将 controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: 中的所有更改保存到三个不同的数组中来解决此问题,插入、删除和更新(您可以使用自定义对象或字典 - 任何适合您的)。当你得到一个move 时,在插入数组和删除数组中都为它添加一个条目。在controllerDidChangeContent: 中,对删除数组进行降序排序,对插入数组进行升序排序。然后应用更改 - 首先删除,然后插入,然后更新。这将解决当您同时进行移动和插入(或移动和删除)时可能发生的崩溃。

    如果您有部分,则还将部分更改保存在数组中,然后按顺序应用更改:删除(降序)、sectionDelete(降序)、sectionInserts(升序)、插入(升序)、更新(任何顺序)。部分不能移动或更新。

    总结:

    1. 有 5 个数组:sectionInserts、sectionDeletes、rowDeletes、rowInserts 和 rowUpdates
    2. 在controllerWillChangeContent 中清除所有数组
    3. 在控制器中:didChangeObject:将 indexPaths 添加到数组中(移动是删除和插入)
    4. 在 controller:didChangeSection 中将部分添加到 sectionInserts 或 rowDeletes 数组中
    5. 在controllerDidChangeContent中:处理如下:

      • 按降序排列行删除
      • 排序部分删除降序
      • 对部分插入升序排序
      • 对 rowInserts 进行升序排序
    6. 然后在一个 performBatchUpdates 块中将更改应用到 collectionView:rowDelete、sectionDelete、sectionInserts、rowInserts 和 rowUpdates 的顺序。

    【讨论】:

    • 所以你建议从 CoreData (NSFetchedResultController) 取消链接 UICollectionView 更新并用简单的数组来计算?
    • 所以你应该继续从委托回调中更新collectionView。但是你保留了 5 个数组,而不是那些 blockOperations:sectionDeletes、sectionInserts、rowDeletes、rowInserts 和 rowUpdates。在 controllerWillChangeContent 中清除它们。在 controller:didChangeObject:atIndexPath 和 controller:didChangeSection: 中添加它们。并在 controllerDidChangeContent 中处理它们(更新视图)。
    • 我做了这个改变。它不能在 100% 的情况下解决问题。当我快速点击搜索按钮并导致 CoreData 对象的许多删除和更新时 - 没有崩溃,但集合停止更新
    • 您是否在 fetchedResultsController 回调之外的任何地方更新 collectionView(甚至是您认为应该不相关的不同部分)?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-16
    • 2010-12-24
    • 1970-01-01
    • 2016-08-27
    • 1970-01-01
    • 2018-10-13
    • 2013-04-26
    相关资源
    最近更新 更多