如何使用Realm和UICollectionView防止NSInternalInconsistencyException

时间:2016-09-27 17:28:08

标签: ios swift uicollectionview realm

我有一个从Realm填充的UICollectionView。有些用户似乎随机获得一个NSInternalInconsistencyException,声明类似

  

无效更新:第0部分中的项目数无效   更新后的现有部分中包含的项目(73)必须是   等于该部分之前包含的项目数   更新(73),加上或减去插入或删除的项目数   从该部分(插入1,删除0)和加号或减号   物品移入或移出该部分(0移入,0移出)。

我的代码基于Realm的集合示例。它选择并过滤一些记录:

self.assets = realm.objects(Asset.self).filter("is_deleted = false")

然后它订阅并处理通知:

self.assetsNotificationToken = self.assets!.addNotificationBlock(){ [weak self] (changes: RealmCollectionChange) in

    guard let collectionView = self!.collectionView else { return }

    guard let strongSelf = self else { return }

    switch changes {

        case .Initial:

            collectionView.reloadData()

        case .Update(let _, let deletions, let insertions, let modifications):

            strongSelf.collectionView?.performBatchUpdates({

                collectionView.insertItemsAtIndexPaths(insertions.map { NSIndexPath(forRow: $0, inSection: 0) })

                collectionView.reloadItemsAtIndexPaths(modifications.map { NSIndexPath(forRow: $0, inSection: 0) })

                collectionView.deleteItemsAtIndexPaths(deletions.map { NSIndexPath(forRow: $0, inSection: 0) })

            }, completion: nil)

        case .Error(let error):

            log.error(error.localizedDescription)
            break

    }

}

计数来自:

override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

    guard assets != nil else {
        return 0;
    }

    return assets!.count

}

我后来切换到RealmGridController

无法找到崩溃的来源,我切换到RealmGridController。它是由Realm核心贡献者编写的软件包,它封装了使用realm + CollectionViews所需的所有标准功能。

它似乎工作,然后我开始看到完全相同的崩溃。

  

致命异常:NSInternalInconsistencyException无效更新:   第0节中的项目数无效。包含的项目数   更新后的现有部分(78)必须等于数字   更新前该部分中包含的项目(78),加或   减去从该部分插入或删除的项目数量(2   插入,0删除)并加上或减去移入的项目数   或超出该部分(0移入,0移出)。

Fatal Exception: NSInternalInconsistencyException
0  CoreFoundation                 0x1839dadb0 __exceptionPreprocess
1  libobjc.A.dylib                0x18303ff80 objc_exception_throw
2  CoreFoundation                 0x1839dac80 +[NSException raise:format:]
3  Foundation                     0x184360154 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:]
4  UIKit                          0x18938b00c -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:]
5  UIKit                          0x18938e464 -[UICollectionView _performBatchUpdates:completion:invalidationContext:tentativelyForReordering:]
6  UIKit                          0x18938e2e0 -[UICollectionView _performBatchUpdates:completion:invalidationContext:]
7  UIKit                          0x188d2c2a4 -[UICollectionView performBatchUpdates:completion:]
8  RealmGridController            0x1014a8340 specialized RealmGridController.controllerDidChangeContent(RBQFetchedResultsController) -> () (RealmGridController.swift:316)
9  RealmGridController            0x1014a687c @objc RealmGridController.controllerDidChangeContent(RBQFetchedResultsController) -> () (RealmGridController.swift)
10 RBQFetchedResultsController    0x100ff8edc __112-[RBQFetchedResultsController calculateChangesWithAddedSafeObjects:deletedSafeObjects:changedSafeObjects:realm:]_block_invoke.433 (RBQFetchedResultsController.m:842)
11 libdispatch.dylib              0x1834254bc _dispatch_call_block_and_release

2 个答案:

答案 0 :(得分:2)

RealmGridController是在Realm支持细粒度通知之前编写的库,因此它实现了许多自定义逻辑以获得相同的结果。我强烈建议你恢复原来的逻辑,因为那个逻辑已深深融入Realm本身。

要记住的一件重要事情是Realm查询结果是活动对象;它们会在每次运行循环迭代时自动更新,以包含发生在其中的任何更改。因此,self.assets的内容应始终与您的集合视图显示的内容直接一对一关联。细粒度通知通知仅用作更新任何过时UI元素的机制,并且在调用时,self.assets已经处于最新状态。

您可能需要提供有关应用在运行时修改self.assets内容的更多信息。如何添加和删除元素?您的应用程序中是否发生了并发?

您需要非常小心,不要进行任何手动UI更新,因为任何意外的偏离集合视图的先前状态以及Realm假设需要更新的内容都会产生这些不一致的异常。

答案 1 :(得分:1)

在我的情况下,我有一个刷新和无限加载的拉力。

当我想刷新时,我正在删除所有对象,以便在Web服务调用之前获取新的新数据。

try! Realm().write {
    let demands = Realm().objects(Demand.self)
    for demand in demands {
        Realm().delete(demand)
    }
}

哪个触发了更改通知,但在有时间为删除操作动画之前,我收到了一个新项目的Web服务响应,创建了NSInternalInconsistencyException

所以我试过了:

try! Realm().write {
    let demands = Realm().objects(Demand.self)
    for demand in demands {
        Realm().delete(demand)  
    }
}
collectionView?.reloadData()

它没有用。然后我最终做了:

try! Realm().write {
    let demands = Realm().objects(Demand.self)
    for demand in demands {
        Realm().delete(demand)
        collectionView?.reloadData()
    }
}

它有效!想法是在调用通知之前重新加载CollectionView。 希望它可以帮到某人。

注意:我在模拟器上没有任何问题,仅在真正的iPhone 6设备iOS 10上。