符合协议类型的协议的Swift集合

时间:2017-07-26 11:22:44

标签: ios swift protocols type-erasure swift4

我有一个问题(可能是关于类型擦除)。想象一下以下场景:

public protocol DataItem {

    associatedtype T
    var action: ((_ item: T) -> Void)? {get}
}

struct UserItem: DataItem {

    typealias T = UserItem

    // Custom Properties
    let name: String

    // Protocol: DataItem
    let action: ((T) -> Void)?
}

struct DriverItem: DataItem {

    typealias T = DriverItem

    // Custom Properties
    let licenseNumber: String

    // Protocol: DataItem
    let action: ((T) -> Void)?
}

let items = [
    UserItem(name: "Dexter", action: { (item) in print(item.name)}),
    DriverItem(licenseNumber: "1234567890", action: { (item) in print(item.licenseNumber)})
]

items.forEach {
    $0.action?($0)
}

我有一个DataItem,它是UITableViewCell的抽象数据项,它有一个应该在选择单元格时调用的action属性。我的问题是如何创建DataItem对象数组,从此列表中选择一个项目(或迭代它)并调用相应的操作,该操作将打印UserItem的名称和DriverItem的许可证编号。但是在上面的实现中,编译器抱怨以下消息:项目列表只能是[Any]类型...

Heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional

这样我就无法调用协议DataItem中声明的动作。我试图绕着类型擦除而不能理解它......

如果有人提出解决方案,会很高兴......

2 个答案:

答案 0 :(得分:-1)

没有额外的要求并没有那么难:

struct UserItem {
    let name: String

    func action() {
        print(name)
    }
}

struct DriverItem {
    let licenseNumber: String

    func action() {
        print(licenseNumber)
    }
}

let data = UserItem(name: "Test")
let closure = { print("Some object data \(data.name)") }
let items = [
    UserItem(name: "Dexter").action,
    DriverItem(licenseNumber: "1234567890").action,
    closure
]

items.forEach {
    $0()
}

答案 1 :(得分:-1)

错误只是要求您向项目添加类型:

let items: Array<Any> = [
    UserItem(name: "Dexter", action: { (item) in print(item.name)}),
    DriverItem(licenseNumber: "1234567890", action: { (item) in print(item.licenseNumber)})
]

不确定这是否会导致更多错误,因为我不确定您是否可以在创建阵列时调用完成块。

修改

我认为你要做的是在调用变量时接听电话。这样做的方法是使用你已输入但未完成的get,如下所示:

var licenseNumber: String {
    get {
        print("doSomething!")
        return self.licenseNumber
    }
}

数组会变成:

let items: Array<Any> = [
    UserItem(name: "Dexter"),
    DriverItem(licenseNumber: "1234567890")
]