【问题标题】:NSFetchedResultsController didChange... delegate crash in UICollectionView?NSFetchedResultsController didChange... UICollectionView 中的委托崩溃?
【发布时间】:2017-03-18 14:53:00
【问题描述】:

我试图理解为什么我的应用程序在像这样实现 NSFetchedResultsController 委托时崩溃:

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

    contentCollectionView.performBatchUpdates({ 
        switch type {
        case .insert:
            guard let insertIndexPath = newIndexPath else { return }
            self.contentCollectionView.insertItems(at: [insertIndexPath])
        case .delete:
            guard let deleteIndexPath = indexPath else { return }
            self.contentCollectionView.deleteItems(at: [deleteIndexPath])
        case .update:
            guard let updateIndexPath = indexPath else { return }
            self.contentCollectionView.reloadItems(at: [updateIndexPath])
        case .move:
            guard let indexPath = indexPath, let newIndexPath = newIndexPath else { return }
            self.contentCollectionView.moveItem(at: indexPath, to: newIndexPath)
        }
    }) { (completed) in

    }

}

控制台显示此崩溃错误:

由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“无效更新:第 0 节中的项目数无效。更新后现有节中包含的项目数 (52) 必须等于更新前该节中包含的项目数 (50),加上或减去从该节插入或删除的项目数(插入 1 个,删除 0 个),加上或减去移入或移出该节的项目数( 0 个搬入,0 个搬出)。

从数据的角度来看,我看不到我做错了什么,核心数据更新正常,但 CollectionView 崩溃,如上所述。

当我重新启动应用程序时,它工作正常。

谁能告诉我我发布的代码是否不是处理委托的正确方法?我可能做错了什么?

我想知道的另一件事是我是否会因为我习惯使用 UITableViews 而不是集合视图而导致崩溃。也就是说,我没有像在 TableView 中那样处理“beginUpdates”或“endUpdates”。 UICollectionView 没有我刚刚发现的那些调用,所以,我是否因为没有正确处理 CollectionView 中更新的开始和结束而导致崩溃?如果是这样,最好的解决方案是什么?

补充: 所以,我尝试了建议的解决方案: https://gist.github.com/nor0x/c48463e429ba7b053fff6e277c72f8ec

它仍然崩溃。

如果我不使用 FRC 委托(因此,如果我根本不设置 FRC 委托)应用程序不会因为我重新加载整个集合视图而崩溃。

知道我可以做些什么来正确使用 FRC 的委托而不是崩溃吗?

加法:

这里还有一点信息: 插入对象的 BlockOperation 实际运行。我已将日志放入块中:

                    blockOperations.append(
                    BlockOperation(block: { [weak self] in
                        dPrint("BlockOperation Insert Object: \(newIndexPath)")
                        if let this = self {
                            DispatchQueue.main.async {
                                this.collectionView!.insertItems(at: [newIndexPath!])
                            }
                        }
                    })
                )

这是在控制台中运行的,所以我知道在集合视图上调用了“插入”(不是空的,此时集合视图不是空部分),但后来我得到了这个:

由于未捕获的异常'NSInternalInconsistencyException'而终止应用程序,原因:'无效更新:第0节中的项目数无效。更新后现有节中包含的项目数(64)必须等于项目数更新前包含在该节中的项目数 (63),加上或减去从该节插入或删除的项目数(0 插入,0 删除),加上或减去移入或移出该节的项目数(0 移入, 0 移出)。

那么,如果插入确实运行了,为什么它会说“...(0 插入,0 删除)...”?

加法:

所以,正如您在上面的最后添加中看到的那样,我在声称插入发生时犯了一个错误。日志 dPrint("BlockOperation Insert Object: (newIndexPath)")DispatchQueue.main.async { 之前,所以我实际上无法声称插入实际运行。然后我放了一个断点,在 DispatchQueue.main.async { 可以执行之前发生了崩溃,所以实际上没有插入!所以,我删除了 DispatchQueue.main.async { 并留下了 this.collectionView!.insertItems(at: [newIndexPath!])NO MORE CRASH !!!

问题是,为什么?有什么想法吗?

【问题讨论】:

    标签: ios uicollectionview nsfetchedresultscontroller


    【解决方案1】:

    我继续实施了自己的解决方案。提出的异步解决方案不起作用,我仍然会崩溃。

    这是现在能够处理 FRC 委托更改以驱动集合视图的大部分代码:

    func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        changes = []
    }
    
    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
    
        guard let dataSource = dataSource else { return }
        guard let collectionView = dataSource.collectionView else { return }
    
        guard collectionView.window != nil else { return }
    
        if type == NSFetchedResultsChangeType.insert {
    
            if collectionView.numberOfSections > 0 {
    
                if collectionView.numberOfItems( inSection: newIndexPath!.section ) == 0 {
                    self.shouldReloadCollectionView = true
                } else {
                    changes?.append(CollectionViewChange(isSection:false, type: type, indexPath: nil, newIndexPath: newIndexPath, sectionIndex:nil))
                }
    
            } else {
                self.shouldReloadCollectionView = true
            }
        }
        else if type == NSFetchedResultsChangeType.update {
            changes?.append(CollectionViewChange(isSection:false, type: type, indexPath: indexPath, newIndexPath:nil, sectionIndex:nil))
        }
        else if type == NSFetchedResultsChangeType.move {
            changes?.append(CollectionViewChange(isSection:false, type: type, indexPath: indexPath, newIndexPath: newIndexPath, sectionIndex:nil))
        }
        else if type == NSFetchedResultsChangeType.delete {
            if collectionView.numberOfItems( inSection: indexPath!.section ) == 1 {
                self.shouldReloadCollectionView = true
            } else {
                changes?.append(CollectionViewChange(isSection:false, type: type, indexPath: indexPath, newIndexPath: nil, sectionIndex:nil))
            }
        }
    }
    
    public func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
        guard let dataSource = dataSource else { return }
        guard let collectionView = dataSource.collectionView else { return }
        guard collectionView.window != nil else { return }
        changes?.append(CollectionViewChange(isSection:true, type: type, indexPath: nil, newIndexPath: nil, sectionIndex:sectionIndex))
    }
    
    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    
    
        guard let dataSource = dataSource else {
            self.changes = nil
            return
        }
        guard let collectionView = dataSource.collectionView else {
            self.changes = nil
            return
        }
    
        guard collectionView.window != nil else { self.changes = nil; return }
    
        // Checks if we should reload the collection view to fix a bug @ http://openradar.appspot.com/12954582
        if (self.shouldReloadCollectionView) {
            collectionView.reloadData()
        } else {
            collectionView.performBatchUpdates({ () -> Void in
                if let changes = self.changes {
                    for change in changes {
                        switch change.type {
                        case .insert:
                            if change.isSection {
                                collectionView.insertSections(NSIndexSet(index: change.sectionIndex!) as IndexSet)
                            } else {
                                collectionView.insertItems(at: [change.newIndexPath!])
                            }
                        case .update:
                            if change.isSection {
                                collectionView.reloadSections(NSIndexSet(index: change.sectionIndex!) as IndexSet)
                            } else {
                                collectionView.reloadItems(at: [change.indexPath!])
                            }
                        case .move:
                            if !change.isSection {
                                collectionView.moveItem(at: change.indexPath!, to: change.newIndexPath!)
                            }
                        case .delete:
                            if change.isSection {
                                collectionView.deleteSections(NSIndexSet(index: change.sectionIndex!) as IndexSet)
                            } else {
                                collectionView.deleteItems(at: [change.indexPath!])
                            }
                            break
                        }
                    }
                }
            }, completion: { (finished) -> Void in
                self.changes = nil
            })
        }
    }
    
    deinit { changes = nil }
    

    【讨论】:

      【解决方案2】:

      uicollectionview 与 nsfetchedresultscontroller 的行为不同。请看一看。这将帮助你 https://gist.github.com/nor0x/c48463e429ba7b053fff6e277c72f8ec

      【讨论】:

      • 我已经实现了那个解决方案,它仍然崩溃。
      猜你喜欢
      • 1970-01-01
      • 2021-10-30
      • 2011-09-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多