7

Diffable 数据源需要指定 aSectionIdentifierType和 anItemIdentifierType并且这些类型必须符合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()甚至没有调用 and == 函数。

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

4

1 回答 1

7

经过一番调查,我发现在引擎盖下UITableViewDiffableDataSource使用。NSOrderedSet在将标识符数组传递给有序集合之前,它被转换为 Objective-C 对象数组(通过Swift._bridgeAnythingToObjectiveC<τ_0_0>(τ_0_0) -> Swift.AnyObject函数)。因为 Swift 和 Objective-C 类共享相同的内存布局,所以它们按原样传递。NSOrderedSet然后依赖于hashisEqual:Objective-C 方法而不是Hashable,并且 Swift 为那些相同的方法提供默认实现,NSObject即使一个类不是从 子类化的NSObject,但没有转发调用Hashable(只有相反的方式)。

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

于 2021-11-16T18:56:26.357 回答