为什么 Diffable 数据源以不同方式对待 class 和结构类型?
Why do Diffable Datasources treat class and struct types differently?
可区分数据源需要指定 SectionIdentifierType
和 ItemIdentifierType
并且这些类型必须符合 Hashable
据说它们必须符合 Hashable
以便数据源可以进行比较。
那么,为什么即使 == 和散列函数相同,它的行为也会根据标识符类型是 class 还是结构而有所不同?或者甚至 === 函数被 classes 覆盖,以便它更像一个值类型?
示例:
import UIKit
public class DebugViewController: UIViewController {
typealias SectionType = IntWrapper
typealias ItemType = IntWrapper
public class IntWrapper: Hashable {
public static func == (lhs: DebugViewController.IntWrapper, rhs: DebugViewController.IntWrapper) -> Bool {
lhs.number == rhs.number
}
public static func === (lhs: DebugViewController.IntWrapper, rhs: DebugViewController.IntWrapper) -> Bool {
lhs.number == rhs.number
}
public func hash(into hasher: inout Hasher) {
hasher.combine(number)
}
var number: Int
init(number: Int) {
self.number = number
}
}
private var dataSource: UITableViewDiffableDataSource<SectionType, ItemType>!
@IBOutlet var tableView: UITableView!
public override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "DefaultCell")
dataSource = UITableViewDiffableDataSource<SectionType, ItemType>(tableView: tableView) { (tableView, indexPath, item) -> UITableViewCell? in
let cell = tableView.dequeueReusableCell(withIdentifier: "DefaultCell")!
cell.textLabel?.text = "\(item.number)"
return cell
}
apply()
}
@IBAction func buttonTapped(_ sender: Any) {
apply()
}
func apply() {
var snapshot = NSDiffableDataSourceSnapshot<SectionType, ItemType>()
let sections = [IntWrapper(number: 0)]
let items = [IntWrapper(number: 1)]
snapshot.appendSections(sections)
sections.forEach { snapshot.appendItems( items, toSection: [=11=]) }
dataSource.apply(snapshot, animatingDifferences: true)
}
}
如果 IntWrapper
是一个结构,那么 table 视图在 apply()
被调用时什么都不做(apply()
基本上加载相同的数据)对我来说,这是预期行为。
如果 IntWrapper
是 class,则 table 视图会在调用 apply() 时重新加载。此外,甚至没有调用 hash()
和 == 函数。
除非有人可以访问源代码(提示,提示)或者除非我在我的示例中犯了一些错误,否则我认为这个问题无法得到解答。
经过一些调查,我发现 UITableViewDiffableDataSource
在幕后使用了 NSOrderedSet
。在将标识符数组传递给有序集之前,它被转换为 Objective-C 对象的数组(通过 Swift._bridgeAnythingToObjectiveC<τ_0_0>(τ_0_0) -> Swift.AnyObject
函数)。因为 Swift 和 Objective-C classes 共享 same memory layout 它们按原样传递。 NSOrderedSet
然后依赖于 hash
和 isEqual:
Objective-C 方法而不是 Hashable
,并且 Swift 提供与 [=] 相同的默认实现17=] 即使 class 不是从 NSObject
子class 编辑的,但是没有转发呼叫到 Hashable
(只有相反的方向)。
也就是说,在可区分数据源中使用 classes 的唯一正确方法是从 NSObject
中子class 或至少实现 hash()
和 isEqual(_:)
带有 @objc
注释的方法。
可区分数据源需要指定 SectionIdentifierType
和 ItemIdentifierType
并且这些类型必须符合 Hashable
据说它们必须符合 Hashable
以便数据源可以进行比较。
那么,为什么即使 == 和散列函数相同,它的行为也会根据标识符类型是 class 还是结构而有所不同?或者甚至 === 函数被 classes 覆盖,以便它更像一个值类型?
示例:
import UIKit
public class DebugViewController: UIViewController {
typealias SectionType = IntWrapper
typealias ItemType = IntWrapper
public class IntWrapper: Hashable {
public static func == (lhs: DebugViewController.IntWrapper, rhs: DebugViewController.IntWrapper) -> Bool {
lhs.number == rhs.number
}
public static func === (lhs: DebugViewController.IntWrapper, rhs: DebugViewController.IntWrapper) -> Bool {
lhs.number == rhs.number
}
public func hash(into hasher: inout Hasher) {
hasher.combine(number)
}
var number: Int
init(number: Int) {
self.number = number
}
}
private var dataSource: UITableViewDiffableDataSource<SectionType, ItemType>!
@IBOutlet var tableView: UITableView!
public override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "DefaultCell")
dataSource = UITableViewDiffableDataSource<SectionType, ItemType>(tableView: tableView) { (tableView, indexPath, item) -> UITableViewCell? in
let cell = tableView.dequeueReusableCell(withIdentifier: "DefaultCell")!
cell.textLabel?.text = "\(item.number)"
return cell
}
apply()
}
@IBAction func buttonTapped(_ sender: Any) {
apply()
}
func apply() {
var snapshot = NSDiffableDataSourceSnapshot<SectionType, ItemType>()
let sections = [IntWrapper(number: 0)]
let items = [IntWrapper(number: 1)]
snapshot.appendSections(sections)
sections.forEach { snapshot.appendItems( items, toSection: [=11=]) }
dataSource.apply(snapshot, animatingDifferences: true)
}
}
如果 IntWrapper
是一个结构,那么 table 视图在 apply()
被调用时什么都不做(apply()
基本上加载相同的数据)对我来说,这是预期行为。
如果 IntWrapper
是 class,则 table 视图会在调用 apply() 时重新加载。此外,甚至没有调用 hash()
和 == 函数。
除非有人可以访问源代码(提示,提示)或者除非我在我的示例中犯了一些错误,否则我认为这个问题无法得到解答。
经过一些调查,我发现 UITableViewDiffableDataSource
在幕后使用了 NSOrderedSet
。在将标识符数组传递给有序集之前,它被转换为 Objective-C 对象的数组(通过 Swift._bridgeAnythingToObjectiveC<τ_0_0>(τ_0_0) -> Swift.AnyObject
函数)。因为 Swift 和 Objective-C classes 共享 same memory layout 它们按原样传递。 NSOrderedSet
然后依赖于 hash
和 isEqual:
Objective-C 方法而不是 Hashable
,并且 Swift 提供与 [=] 相同的默认实现17=] 即使 class 不是从 NSObject
子class 编辑的,但是没有转发呼叫到 Hashable
(只有相反的方向)。
也就是说,在可区分数据源中使用 classes 的唯一正确方法是从 NSObject
中子class 或至少实现 hash()
和 isEqual(_:)
带有 @objc
注释的方法。