【问题标题】:Why do Diffable Datasources treat class and struct types differently?为什么 Diffable Datasources 以不同的方式对待类和结构类型?
【发布时间】:2021-07-23 06:02:08
【问题描述】:

Diffable 数据源需要指定 SectionIdentifierTypeItemIdentifierType,这些类型必须符合 Hashable

假设它们必须符合Hashable,以便数据源可以进行差异化。

那么为什么即使 == 和散列函数相同,它的行为也会根据标识符类型是类还是结构而有所不同呢?或者甚至为类重写 === 函数,使其更像值类型?

例子:

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: $0) }
        dataSource.apply(snapshot, animatingDifferences: true)
    }
}

如果IntWrapper 是一个结构,则在调用apply() 时表视图什么也不做(apply() 本质上是加载相同的数据)对我来说,这是预期的行为。

如果IntWrapper 是一个类,则在调用apply() 时表视图会重新加载。此外,甚至没有调用 hash() 和 == 函数。

除非有人可以访问源(提示、提示)或除非我在示例中犯了错误,否则我认为无法回答此问题。

【问题讨论】:

  • 你有没有想过?我被困了 2 天试图弄清楚这一点
  • @gmogames 我只坚持尽可能使用structs。当我向每个结构添加let id: UUID 时,如果两个结构的数据可以相同,以便它们始终是唯一的。如果您获得两个具有相同数据的结构,则应用程序将崩溃。如果需要更新数据,我会创建一个具有相同 id 的新结构,但会更改值并将其添加到新快照中。

标签: ios swift diffabledatasource


【解决方案1】:

经过一番调查,我发现UITableViewDiffableDataSource 在后台使用NSOrderedSet。在将标识符数组传递给有序集合之前,它被转换为一个 Objective-C 对象数组(通过Swift._bridgeAnythingToObjectiveC&lt;τ_0_0&gt;(τ_0_0) -&gt; Swift.AnyObject 函数)。因为 Swift 和 Objective-C 类共享same memory layout,所以它们按原样传递。然后NSOrderedSet 依赖于hashisEqual: Objective-C 方法而不是Hashable,并且Swift 提供了与NSObject 相同的默认实现,即使一个类不是NSObject 的子类,但是没有转接到Hashable 的呼叫(反之亦然)。

也就是说,在 diffable 数据源中使用类的唯一正确方法是从 NSObject 子类化它们,或者至少使用 @objc 注释实现 hash()isEqual(_:) 方法。

【讨论】:

  • 非常有趣,但它如何回答实际提出的问题?
  • @matt 我扩展了答案。 NSObjectsHashable 的实现是转发到 hashisEqual: 但不是相反。
  • @NickolayTarbayev 很好的回答,非常感谢。我对你的调查方式很感兴趣。
  • @BryanBryce 我刚刚为 struct items 案例设置了几个断点,并在调用堆栈中挖掘了汇编代码以寻找一些有意义的符号。在那里我找到了NSOrederedSet_bridgeAnythingToObjectiveC 函数。然后我通过在用作项目的 Swift 类中实现 Obj-C 方法来验证我的假设。
猜你喜欢
  • 1970-01-01
  • 2022-10-19
  • 2013-01-12
  • 2018-09-23
  • 2016-04-15
  • 2016-04-17
  • 1970-01-01
  • 2021-07-15
  • 2018-02-15
相关资源
最近更新 更多