【问题标题】:Swift Array of Tuples with Generic tuple elements具有通用元组元素的 Swift 元组数组
【发布时间】:2019-07-12 20:17:34
【问题描述】:

需要帮助使用 Swift 对以下内容进行建模。我想构建一个元组数组Array<Tuple>,每个元组包含三个元素

  1. 一个字符串字段
  2. Array<SomeElement>AnyRealmCollection<SomeObject>
  3. 作用于特定 SomeElementSomeObject 的闭包。

    例如,Array<Tuple> 可以捕获:

    [("section1", AnyRealmCollection<Car>(), {}),
     ("section2", Array<Driver>(), {},
     ("section3", AnyRealmCollection<Passenger>(), {}
    ]

然后,在一个单独的方法中,forEach Array 中的 Tuple,相应的闭包被触发来处理Array&lt;SomeElement&gt;AnyRealmCollection&lt;SomeObject&gt; 中的每一个 SomeElement/SomeObject。

使用这个模型,我希望能够以类型安全的方式轻松交换其他 SomeElement/SomeObject,向Array&lt;Tuple&gt; 添加新条目。

有什么建议吗?

【问题讨论】:

  • 您不能拥有异构数组,因此除非您将数组声明为 Array&lt;Any&gt;,否则这是行不通的,您不应该这样做,因为在大多数情况下,如果您需要使用 Any 您'没有正确解决问题。
  • 从结构开始,而不是元组。

标签: arrays swift generics


【解决方案1】:

您不能拥有上述不同类型的数组,但您可以使用数组中的元素需要遵守的协议。但据我所知,您不能使元组符合协议,但结构可以。

让我们为数组项创建一个协议

protocol TupleHandler {
    func iterate() -> Void
}

以及符合协议并模仿您的元组的结构

struct TupleStruct<T> : TupleHandler {
   var string: String
   var array: [T]
   var action: (T)->Void

   func iterate() {
       array.forEach({ action($0) })
   }
}

然后就可以这样使用了

let ts1 = TupleStruct<String>(string: "abc", array: ["A", "B", "C"], action: { print($0) })
let ts2 =  TupleStruct<Int>(string: "def", array: [3, 5, 7], action: { print($0 * 2) })
let array: [TupleHandler] = [ts1, ts2]

for item in array {
    item.iterate()
}

【讨论】:

    【解决方案2】:

    您的需求列表非常好,可以直接转化为类型。首先,让我们完全按照您所说的那样拼写出来。在 Swift 中,任何你会说“and”的地方都是一个结构体(或类或元组,但在本例中是一个结构体),而任何你会说“or”的地方都是一个枚举。

    struct Model<Element> {
    
        // 1. a String field
        let string: String
    
        // 2. either an Array<SomeElement>, or AnyRealmCollection<SomeObject>
        enum Container {
            case array([Element])
            case realmCollection(AnyRealmCollection<Element>)
        }
        let elements: [Container]
    
        // 3. a closure that acts upon the specific SomeElement or SomeObject.
        let process: (Element) -> Void
    }
    

    编辑:实际上不可能在这里创建 .realmCollection 案例,因为 AnyRealmCollection 对 Element 提出了额外的要求。要解决这个问题,您必须围绕 AnyRealmCollection 构建一个额外的包装器,以消除该要求。 It's not hard, but it's tedious, 除非你真的需要区分 AnyRealmCollection 和 AnyCollection,否则我会避免这种情况。


    “数组或领域集合”可能比您真正的意思更精确。我怀疑你的意思是“元素的集合”。如果这就是你的意思,那么我们可以更容易地说出来:

    struct Model<Element> {
        let string: String
        let elements: AnyCollection<Element>
        let process: (Element) -> Void
    }
    

    除了强制调用者将元素包装在AnyCollection 中,我还提供以下初始化:

    init<C>(string: String, elements: C, process: @escaping (Element) -> Void)
        where C: Collection, C.Element == Element {
            self.string = string
            self.elements = AnyCollection(elements)
            self.process = process
    }
    

    通常,在 Swift 中避免使用元组,除非它用于非常简单和短暂的事情。 Swift 元组非常有限,制作一个结构太简单了,不能浪费时间在元组的所有限制上。

    但是,这种类型不能放入具有不同元素的数组中。如果这是您使用 Model 的唯一方式,并且您无需将元素取出,则可以将其设为非泛型:

    struct Model {
        let string: String
        let process: () -> Void
    
        init<C: Collection>(_ string: String,
                            _ elements: C,
                            _ process: @escaping (C.Element) -> Void) {
            self.string = string
            self.process = { elements.forEach(process) }
        }
    }
    
    let list = [Model("section1", AnyRealmCollection<Car>(), { _ in return }),
                Model("section2", Array<Driver>(), { _ in return }),
                Model("section3", AnyRealmCollection<Passenger>(), { _ in return })
    ]
    

    如果有时您需要一个模型,有时您需要在不知道元素的情况下将其放入数组中,那么您可以构建一个类型橡皮擦:

    struct AnyModel {
        let string: String
        let process: () -> Void
        init<Element>(_ model: Model<Element>) {
            self.string = model.string
            self.process = { model.elements.forEach(model.process) }
        }
    }
    

    【讨论】:

    • 谢谢,我之前尝试过枚举容器,AnyRealmCollection&lt;Element&gt; 需要 Element:RealmSwift.Object,而 Realm Object on Array 的动态特性可能存在一些运行时问题。这应该如何建模?
    • 我不确定您在这里所说的“数组上领域对象的动态特性”是什么意思。通常不需要对 AnyRealmCollection 的要求进行建模以接受一个作为参数。调用者首先构造了 AnyRealmCollection 的事实证明它的类型要求已经得到满足。您无需重新执行该要求。
    • 刚才试了枚举Container,caserealmCollection(AnyRealmCollection&lt;Element&gt;)导致Type 'Element'不符合协议'RealmCollectionValue'
    • 在这里,icanzilb 在 2016 年 12 月 9 日指出 RxRealm 对象可能存在问题。 github.com/RxSwiftCommunity/RxDataSources/issues/…
    • 已更新;是的,您对类型要求问题是正确的。但是,我不确定 icanzlib 的 cmets 如何适用于这种情况(除了您正在以一种可能永远不应该进入相同数据结构的方式混合值类型和“更改背后的”引用类型)。
    猜你喜欢
    • 2014-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-07
    • 2019-12-06
    • 2020-07-09
    • 2019-01-14
    • 2017-01-04
    相关资源
    最近更新 更多