【问题标题】:Heterogeneous Array of generic object where T is differentT 不同的通用对象的异构数组
【发布时间】:2019-01-24 15:18:27
【问题描述】:

这就是我的类BaseSection 实现Collection 的方式,它看起来像这样:

class BaseSection<T:Equatable> {

var items:[T]?

required init(items:[T]){
   self.items = items
  }
}

let sectionTop = BaseSection<TopItem>(items:["foo","bar","yo"])
let sectionBottom = BaseSection<BottomItem>(items:["foo","bar","yo"])

对于这个演示,我正在构造字符串对象部分,它们是Equatable,但我的项目构造对象的一个​​属性是String 此处未列出集合实现。 我需要将两个对象都保存在数组中,即使它们的基类相同,也不能这样做。

let sections = [sectionTop, sectionBottom]
heterogeneous collection literal could only be inferred to '[Any]' add explicit type annotation....

我与许多解决方案斗争了几个小时,但在这里无法实现我的目标。 任何帮助将不胜感激

Edit: I have uploaded the sample code to gitHub so you can get a better grasp of what im doing 

github Link

【问题讨论】:

  • 如果你有sections,你打算用它做什么?您将无法访问其元素的 items 属性,因为类型不一致。 (包含as? 的解决方案将所有内容都转换为错误,应该重新考虑。)了解您期望使用sections 做什么(特别是您打算如何处理它的元素),我们可以讨论它的类型应该是。
  • @user28434 有趣的文章 :)
  • 我同意 Rob 的观点,不清楚你想用数组做什么
  • 这不是我真正要问的。我的意思是从字面上看,您认为自己编写了哪一行代码来消耗sections?您将无法访问items(至少不能像TopItemBottomItem),那么您将如何处理它呢?您的调用代码将决定您如何构建它。 (这是怎么回事,你几乎可以肯定这里确实想要一个协议,但我必须知道你希望该协议做什么。)

标签: swift generics collections


【解决方案1】:

在swift自定义泛型中becos都是不变的,也就是说BaseSection&lt;TopItem&gt;BaseSection&lt;BottomItem&gt;没有关系,所以两者唯一的共同类型是Any,你可以点击评论中的链接了解更多信息,作为总结

  1. 数组/字典是协方差的
  2. 闭包参数类型是逆变,闭包返回类型是协变
  3. 自定义泛型类型是不变的

另外,Arrays/Dict 是 struct,这意味着它们是按值传递的,因此将它们视为协方差是安全的

【讨论】:

    【解决方案2】:

    您似乎正在构建一种机制,用于在数据更改时自动更改表视图。很常见。我强烈建议您查看ListDiff,以获得计算更改的有用起点。 (在我自己的代码中,我使用了 ListDiff 的重写版本,所以我这里的示例没有经过测试,但它们应该非常接近。)

    目标是创建一组更新操作(复制自 ListDiff 示例):

    import ListDiff
    
    extension Int : Diffable {
        public var diffIdentifier: AnyHashable {
            return self
        }
    }
    let o = [0, 1, 2]
    let n = [2, 1, 3]
    let result = List.diffing(oldArray: o, newArray: n)
    // result.hasChanges == true
    // result.deletes == IndexSet(integer: 0)
    // result.inserts == IndexSet(integer: 2)
    // result.moves == [List.MoveIndex(from: 2, to: 0), List.MoveIndex(from: 1, to: 1)]
    // result.changeCount == 4
    

    这里重要的是你有一系列的删除、插入和移动操作。您可以将这些应用到表格视图中,其中包含以下内容:

    extension UITableView {
        func applyChanges(from changeset: Result, forSection section: Int = 0) {
            beginUpdates()
    
            let deletes = changeset.deletes.map { IndexPath(row: $0, section: section) }
            deleteRows(at: deletes, with: .bottom)
    
            let inserts = changeset.deletes.map { IndexPath(row: $0, section: section) }
            insertRows(at: inserts, with: .none)
    
            for move in changeset.moves {
                guard !changeset.deletes.contains(move.from) else { continue }
                moveRow(at: IndexPath(row: move.from, section: section), 
                        to: IndexPath(row: move.to, section: section))
            }
    
            endUpdates()
        }
    }
    

    这里的关键点是,对于动画,您只需要索引路径。您实际上并不需要这些数据。所以计算索引路径并传递它们。不需要泛型。

    【讨论】:

    • 感谢您的回答!你注意到我数组的结构了吗?它包含 2 个具有不同对象的数组。整数数组很容易实现
    • 如果你介意看一下github.com/OrAzr/DiffingSections
    • 我不确定如何处理您发布的代码。这里还有问题吗?那里似乎不需要多种类型的数组。我什至不明白为什么 BaseSection 是通用的;它只持有 [BaseItem],所以让它持有 [BaseItem]。您在这里混合了协议、子类化和泛型。这太复杂了,会一次又一次地咬你。如果您需要子类,只需使用子类。没有必要在此之上引入泛型和协议。
    • 对标题问题的简短回答是:你不能那样做,重新设计所以你不需要。 (有很多通用的 tableview 处理程序不需要这样做;我自己在 Swift 中构建它们。如果您不想构建自己的,请参阅github.com/Instagram/IGListKit 以获得非常强大的一个。它在 ObjC 中,但与/ Swift 非常好。)
    猜你喜欢
    • 1970-01-01
    • 2016-04-19
    • 2015-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 2018-09-15
    相关资源
    最近更新 更多