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

Ios DiffableDataSource抛出“;致命:提供的标识符不唯一。”;在结构内包装MPMediaItem时,ios,swift,diffabledatasource,Ios,Swift,Diffabledatasource,我使用UITableView diffabledatasource和UITableView来显示音乐库中的歌曲。该代码运行良好: let tracks:[MPMediaItem]=MPMediaQuery.songs().items??[] self.dataSource.apply(节:0,项:跟踪) 但是,当我将MPMediaItem包装到自定义Track结构中时,出现了以下错误:致命:提供的标识符不唯一。 struct Track:equalable,Hashable{ let项目:MP

我使用
UITableView diffabledatasource
UITableView
来显示音乐库中的歌曲。该代码运行良好:

let tracks:[MPMediaItem]=MPMediaQuery.songs().items??[]
self.dataSource.apply(节:0,项:跟踪)
但是,当我将
MPMediaItem
包装到自定义
Track结构中时,出现了以下错误:
致命:提供的标识符不唯一。

struct Track:equalable,Hashable{
let项目:MPMediaItem
变量标题:字符串?{item.title}
初始化(项目:MPMediaItem){
self.item=项目
}
}
让items=MPMediaQuery.songs().items??[]
let tracks:[Track]=items.map{Track(item:$0)}
self.dataSource.apply(节:0,项:跟踪)
MPMediaItem
已经符合了
equalable
Hashable
的要求,因此我认为如果我在另一个同样符合
equalable
Hashable
的结构中使用它,应该没问题(
跟踪结构

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

扩展UITableViewDiffableDataSource{ func apply(节:SectionIdentifierType,项:[ItemIdentifierType],动画差异:Bool=false){ var snapshot=NSDiffableDataSourceSnapshot() snapshot.appendSections([节]) snapshot.appendItems(项目) 应用(快照、动画差异:动画差异) } }
更新2:在我符合
跟踪
可识别
协议后,它起作用了:

struct Track:equalable、Hashable、Identifiable{
let项目:MPMediaItem
let id:MPMediaEntityPersistentID
变量标题:字符串?{item.title}
初始化(项目:MPMediaItem){
self.item=项目
self.id=item.persistentID
}
}
甚至将
标题更改为存储属性也可以正常工作,没有任何错误:

struct Track:equalable,Hashable{
let项目:MPMediaItem
标题:字符串?
初始化(项目:MPMediaItem){
self.item=项目
self.title=item.title
}
}

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

我猜MPMediaItem的哈希性中有一个bug。这可能会导致您在所描述的两种情况下得到不同的答案。在本例中,我将故意创建一个buggy 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)
    }
var set=set()
设dh1=狗托(狗:狗(名称:“漫游者”))
设dh2=狗托(狗:狗(名称:“漫游者”))
set.insert(dh1)
set.insert(dh2)
打印(设置计数)
做{
var set=set()
让dh1=狗(名称:“漫游者”)
让dh2=狗(名称:“漫游者”)
set.insert(dh1)
set.insert(dh2)
打印(设置计数)
}
反复运行测试。有时我得到1和2。有时我得到2和1。有时我得到1和1。有时我会撞车


我不知道确切的问题是什么,但很明显,将NSObject哈希性暴露在Swift的哈希性要求之下会暴露出错误。我建议向苹果公司报告这一点,同时继续使用一种解决方法,比如您的标识符。

我猜MPMediaItem的哈希功能中有一个bug。这可能会导致您在所描述的两种情况下得到不同的答案。在本例中,我将故意创建一个buggy 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)
    }
var set=set()
设dh1=狗托(狗:狗(名称:“漫游者”))
设dh2=狗托(狗:狗(名称:“漫游者”))
set.insert(dh1)
set.insert(dh2)
打印(设置计数)
做{
var set=set()
让dh1=狗(名称:“漫游者”)
让dh2=狗(名称:“漫游者”)
set.insert(dh1)
set.insert(dh2)
打印(设置计数)
}
反复运行测试。有时我得到1和2。有时我得到2和1。有时我得到1和1。有时我会撞车


我不知道确切的问题是什么,但很明显,将NSObject哈希性暴露在Swift的哈希性要求之下会暴露出错误。我建议将此情况报告给苹果公司,同时继续使用诸如您的标识符之类的解决方法。

对不起,我忘了这一点。我已经更新了我的问题。谢谢好吧,现在我很好奇。如果您完全省略title属性会怎么样?嗨@matt,我用两个有效的解决方案再次更新了我的问题。但是我不知道为什么只使用
MPMediaItem
作为存储属性,而不使用任何其他存储属性,如
id
title
抛出错误。您应该通过哈希函数确认哈希值抱歉,我忘了这一点。我已经更新了我的问题。谢谢好吧,现在我很好奇。如果您完全省略title属性会怎么样?嗨@matt,我用两个有效的解决方案再次更新了我的问题。但是我不知道为什么只使用
MPMediaItem
作为存储属性,而不使用任何其他存储属性,如
id
title
抛出错误。您应该通过哈希函数确认哈希值
hash
的默认
NSObject
实现使用对象的指针值。因此,如果您重写
isEqual
以检查基于数据成员的相等性,则还必须重写
hash
,否则您将违反
A==B的要求→ 散列(A)=散列(B)
。也许苹果的工程师们在为
MPMediaItem
@TamásZahola Good theory重写
isEqual
时也犯了同样的错误;当然,我确实想到过这样的事情,但不管它是什么,我都无法证明它。
NSObject
的默认
hash
实现使用对象的指针值。因此,如果覆盖