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 【问题描述】:我使用UITableviewDiffableDataSource
和UITableView
来显示音乐库中的歌曲。这段代码运行良好:
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
已经符合 Equatable
和 Hashable
所以我认为如果我在另一个也符合 Equatable
和 Hashable
(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
作为商店属性而没有任何其他商店属性,如id
和title
抛出错误。
你应该通过哈希函数确认哈希
【参考方案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 - 无动画,但在删除操作期间闪烁
instrumentation.retransformClasses() 抛出致命错误