具有通用元组元素的元组的快速数组

时间:2019-02-19 10:45:04

标签: arrays swift generics

需要帮助使用Swift建立以下模型。我想构建一个元组数组Array<Tuple>,该数组每个都有三个元素

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

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

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

然后,使用另一种方法,对于Array中每个元组,触发相应的闭包以处理Array<SomeElement>AnyRealmCollection<SomeObject>中的每个SomeElement / SomeObject。

使用此模型,我希望能够交换其他SomeElement / SomeObject,并以类型安全的方式轻松地向Array<Tuple>添加新条目。

有什么建议吗?

2 个答案:

答案 0 :(得分: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()
}

答案 1 :(得分:0)

您的需求列表非常好,并且可以直接转换为类型。首先,让我们完全按照您说的说出来。在任何您要说“和”的地方都是一个结构(或类或元组,但在这种情况下是一个结构),在任何您要说“或”的地方都是Swift中的枚举。

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元组非常有限,并且构造一个结构太简单,以至于无法在元组的所有限制上浪费时间。

但是,不能将该类型放入具有不同Elements的数组中。如果这是使用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) }
    }
}