iOS 13上TableView错误的可疑数据源:移动关联不一致

Fab*_*oft 6 objective-c uitableview ios13

我正在将当前的UITableview更新为iOS 13提供的可扩散数据源UITableViewDiffableDataSource

我有一个带有自定义对象的数组(实现isEqual:方法)。在viewWillAppear上,我从磁盘加载数据并致电申请快照。

-(void)updateTableViewAnimated:(BOOL)animated API_AVAILABLE(ios(13.0)){
    NSDiffableDataSourceSnapshot *snapshot = [[NSDiffableDataSourceSnapshot alloc]init];
    [snapshot appendSectionsWithIdentifiers:@[@"sectionTitle"]];
    [snapshot appendItemsWithIdentifiers:self.playlists];
    [self.diffDataSource applySnapshot:snapshot animatingDifferences:animated];
}
Run Code Online (Sandbox Code Playgroud)

一切都加载了。但是,当尝试从数组中删除项目并再次调用时updateTableViewAnimated:,出现异常。

***由于未捕获的异常“ NSInvalidArgumentException”而终止应用程序,原因:“移动关联不一致”

这是什么意思?我该如何解决?

Jas*_*ien 37

更新答案

我很幸运地得到了对我就此提出的错误的回应。事实证明,我的模型对象进行了不正确的哈希和相等检查。

我的 swift 结构符合 Hashable,但提供了 Equatable 的自定义实现,因此我只比较 ID 属性来确定相等性。这意味着两个对象可能被认为是相等的,但具有不同的哈希值,这会混淆差异算法。

为了解决这个问题,我简单地删除了 Equatable 的自定义实现,并使用了合成版本。

您在问题中声明您实现isEqual了 ObjC,类似于 Swift 的==,但是您可能没有提供在所有情况下hash都与您的isEqual实现一致的实现。


原始答案(对于这种情况可能不正确,但如果队列是问题,可能仍然有用)

我不知道这是否与您遇到的问题相同,但在我的情况下,它是由applySnapshot从不同队列调用的方法引起的。

高级数据源 WWDC 会话提到applySnapshot必须在后台队列或主队列上独占调用,但不要从两者调用。高级数据源 WWDC 2019 - 32:00

高级数据源 WWDC 2019

在我的例子中,我使用一个 Combine 发布者来对我的数据源上的变化做出反应,并且该发布者有时会在主线程或后台线程上发送值。为了解决我的问题,我添加.receive(on: RunLoop.Main)到链中。

在您的情况下,也许您可​​以使用您希望它在其上运行的队列(无论是主要还是后台)将任何调用的内容包装updateTableViewAnimated:dispatch_async调用中。


Mos*_*sam 10

添加到 Jasarien 信息丰富的答案中,您需要记住UICollectionViewDiffableDataSource使用 hashable 来区分数据源中的项目

如果您的模型是一个结构体,并且您的数据源模型中有两个项目恰好具有完全相同的值,则Hashable协议将为它们生成相同的 hashValue。

Equatable协议也将返回真!,这会混淆 UICollectionViewDiffableDataSource

解决方案

  • 确保您的数据源没有任何重复项
  • 如果您无法避免重复,并且您不介意刹车Equatable,您可能希望在数据源中添加随机值,但不建议这样做,因为生成的值可能在任何巧合下都相等,如文档 * 低概率中所述,但它可能会发生 *

根据范围的大小和跨度,某些具体值可能比其他值更频繁地表示。

  • 苹果在这里表现得很愚蠢。不同细胞有相同的数据是正常的。在某些情况下,表视图会在不同的单元格中显示相同的数据。如果我添加随机数,那么不会有移动,但会针对所有数据更改情况进行插入和删除 (2认同)