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

Diffable data source for TableView error on iOS 13: Inconsistent associations for moves

我正在将当前的 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];
}

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

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Inconsistent associations for moves'

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

更新答案

我很幸运能够对我提出的有关此问题的错误做出回应。事实证明,我的模型 object 的哈希和相等性检查不正确。

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

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

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


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

我不知道这是否与您遇到的问题相同,但就我而言,这是由从不同队列调用 applySnapshot 方法引起的。

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

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

在你的情况下,也许你可以在 dispatch_async 调用中包装任何调用 updateTableViewAnimated: 的东西,使用你希望它在 运行 上的队列(主要或背景)。

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

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

另外 Equatable 协议将 return 成真! , 这会混淆 UICollectionViewDiffableDataSource

解决方案

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

Depending on the size and span of range, some concrete values may be represented more frequently than others.