DiffableDataSource 抛出“致命:提供的标识符不是唯一的。”将 MPMediaItem 包装在结构中时

Posted

技术标签:

【中文标题】DiffableDataSource 抛出“致命:提供的标识符不是唯一的。”将 MPMediaItem 包装在结构中时【英文标题】:DiffableDataSource throws "Fatal: supplied identifiers are not unique." when wrapping MPMediaItem inside a struct 【发布时间】:2020-04-10 00:50:25 【问题描述】:

我使用UITableviewDiffableDataSourceUITableView 来显示音乐库中的歌曲。这段代码运行良好:

let tracks: [MPMediaItem] = MPMediaQuery.songs().items ?? []
self.dataSource.apply(section: 0, items: tracks)

但是当我将MPMediaItem 包裹在自定义Track struct 中时,我收到了这个错误:Fatal: supplied identifiers are not unique.

struct Track: Equatable, Hashable 
  let item: MPMediaItem

  var title: String?  item.title 

  init(item: MPMediaItem) 
    self.item = item
  

let items = MPMediaQuery.songs().items ?? []
let tracks: [Track] = items.map  Track(item: $0) 
self.dataSource.apply(section: 0, items: tracks)

MPMediaItem 已经符合 EquatableHashable 所以我认为如果我在另一个也符合 EquatableHashable (Track struct) 的结构中使用它应该没问题。

更新 1:apply(section:items:) 是我添加到 UITableViewDiffableDataSource 的扩展名,方便:

extension UITableViewDiffableDataSource 
  func apply(section: SectionIdentifierType, items: [ItemIdentifierType], animatingDifferences: Bool = false) 
    var snapshot = NSDiffableDataSourceSnapshot<SectionIdentifierType, ItemIdentifierType>()
    snapshot.appendSections([section])
    snapshot.appendItems(items)
    apply(snapshot, animatingDifferences: animatingDifferences)
  

更新 2:在我将 Track 符合 Identifiable 协议后它起作用了:

struct Track: Equatable, Hashable, Identifiable 
  let item: MPMediaItem

  let id: MPMediaEntityPersistentID

  var title: String?  item.title 

  init(item: MPMediaItem) 
    self.item = item
    self.id = item.persistentID
  

甚至更改 title 以存储属性也可以正常工作:

struct Track: Equatable, Hashable 
  let item: MPMediaItem

  let title: String?

  init(item: MPMediaItem) 
    self.item = item
    self.title = item.title
  

是什么让这些案例之间如此不同?为什么在使用MPMediaItem 作为Track 结构的唯一存储属性时会出现错误?提前致谢!

【问题讨论】:

对不起,我忘记了。我已经更新了我的问题。谢谢! 好吧,现在我很感兴趣。如果完全省略 title 属性会怎样? 嗨@matt,我已经用两个有效的解决方案再次更新了我的问题。但我不知道为什么只使用MPMediaItem 作为商店属性而没有任何其他商店属性,如idtitle 抛出错误。 你应该通过哈希函数确认哈希 【参考方案1】:

我猜测 MPMediaItem 的哈希性存在错误。这可能会导致您对所描述的两种情况得到不同的答案。在这个例子中,我会故意制作一个有问题的 NSObject:

class Dog : NSObject 
    let name : String?
    init(name:String?) self.name = name
    override func isEqual(_ object: Any?) -> Bool 
        if let dog = object as? Dog 
            return self.name == dog.name
        
        return false
    


struct DogHolder : Hashable 
    let dog : Dog
    var name : String?  dog.name 

这是一个测试:

    var set = Set<DogHolder>()
    let dh1 = DogHolder(dog:Dog(name:"rover"))
    let dh2 = DogHolder(dog:Dog(name:"rover"))
    set.insert(dh1)
    set.insert(dh2)
    print(set.count)

    do 
        var set = Set<Dog>()
        let dh1 = Dog(name:"rover")
        let dh2 = Dog(name:"rover")
        set.insert(dh1)
        set.insert(dh2)
        print(set.count)
    

一遍又一遍地运行测试。有时我得到 1 和 2。有时我得到 2 和 1。有时我得到 1 和 1。有时我崩溃。

我不知道确切的问题是什么,但显然将 NSObject 哈希性暴露给 Swift 的哈希性要求会暴露该错误。我建议将此情况报告给 Apple,同时继续使用您的标识符等解决方法。

【讨论】:

NSObject 的默认 hash 实现使用对象的指针值。因此,如果您重写isEqual 来检查基于数据成员的相等性,您也必须重写hash,否则您将违反A == B → hash(A) == hash(B) 的要求。也许 Apple 工程师在为 MPMediaItem 覆盖 isEqual 时犯了同样的错误。 @TamásZahola 好理论;当然,我确实想过这样的事情,但无论是什么,我都无法证明。

以上是关于DiffableDataSource 抛出“致命:提供的标识符不是唯一的。”将 MPMediaItem 包装在结构中时的主要内容,如果未能解决你的问题,请参考以下文章

UICollectionViewDropDelegate 和 DiffableDataSource

DiffableDataSource - 无动画,但在删除操作期间闪烁

PHPUnit 显示抛出致命错误异常的传递方法

instrumentation.retransformClasses() 抛出致命错误

EclipseLink DataModifyQuery 不会抛出致命异常

致命:异常未在 C++ 中重新抛出