将 MPMediaItem 包装在结构中时,DiffableDataSource 抛出 "Fatal: supplied identifiers are not unique."

DiffableDataSource throws "Fatal: supplied identifiers are not unique." when wrapping MPMediaItem inside a struct

我使用 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: [=12=]) }
self.dataSource.apply(section: 0, items: tracks)

MPMediaItem 已经符合 EquatableHashable 所以我认为如果我在另一个也符合 Equatable 和 [=23 的结构中使用它应该没问题=] (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:在我遵守 TrackIdentifiable 协议后它起作用了:

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 结构的唯一存储 属性 时出现错误?提前致谢!

我猜测 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,同时继续使用变通方法,例如您的标识符。