2017-07-26 77 views
1

我有一个问题(也许关于类型擦除)。想象一下以下情形:符合相关协议的对象的快速收集

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一个抽象的数据项,其中有一个动作属性选择单元格时被调用。我的问题是如何创建一个DataItem对象数组,从这个列表中选择一个项目(或迭代它)并调用相应的操作,它将打印UserItem的名称和DriverItem的许可证编号。但是,与编译器上面实现以下消息抱怨说,项目清单只能是类型[任何] ...

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

这样我可以不叫在协议的DataItem声明的行动。我试图环绕型擦除我的头,但不明白它尚未......如果有人想出了一个解决方案

会很高兴......

+0

这看起来有点怪异,你'associatedtype'在'UserItem'(/'DriverItem /)'是'UserItem'(/'DriverItem /)本身,这意味着_instance_close'action'采用与拥有它的实例相同类型的单个参数。这是否如预期那样,例如给定的'UserItem'实例是对'UserItem'的_another_实例执行一个动作?我希望这个动作可以在实例本身而不是辅助外部实例上执行,但也许这是一个预期的设计选择。无论如何,如果是这样,你可以放弃'associatedtype',只需使用'Self'而不是'T'。 – dfri

+0

它与UIControls中的目标/动作一样,旨在为操作方法中的发件人提供服务。发件人是项目本身。我希望能够在操作方法中使用具体类型的功能(如name和licenseNumber) - 你知道吗?重要的是,这些项目只存在于一个DataSource对象内,它不知道具体的类型。这就是为什么我不能直接访问具体类型的功能。我会尽快尝试你的方法。非常感谢:-) – blackjacx

+0

Arg ....使用Self而不是T行为相同:'Protocol'DataItem'只能用作通用约束,因为它具有Self或关联类型要求。由于这种行为类型橡皮擦被写入。但我不明白如何正确地做到这一点......任何人的想法? – blackjacx

回答

-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() 
} 
+0

动作闭包被认为是可以从外部设置而不是静态函数的CLOSURE。 – blackjacx

+0

您可以将具有正确签名的任何闭包放入数组中 - 您只能使用具有不同参数的闭包。你也可以把一个标题(例子)和一个void-closure的结构放在适当的数组中。 – Tino

-1

的错误只是要求你一个类型添加到项目:

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

不知道这会不会导致更多的错误,因为我不知道,如果你可以调用完成块得到控制而e创建阵列。


编辑

我认为你正在尝试做的就是打电话时,该变量调用。做到这一点的方法是使用你已经输入的get但没有完整的,就像这样:

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

的阵列将成为类似:

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

不能调用动作块... – blackjacx

+0

@blackjacx我编辑了我的回答 –

+0

仍然无法调用诸如'items.forEach {0}($ 0) }之类的动作方法,因为项目包含任何对象你的情况。我想橡皮擦模式是我搜索的,但我不知道如何正确实现这一点。 – blackjacx