【发布时间】:2016-07-03 01:15:07
【问题描述】:
protocol ParentProtocol { }
protocol ChildProtocol: ParentProtocol { }
protocol Child_With_Value_Protocol: ParentProtocol {
associatedType Value
func retrieveValue() -> Value
}
试图创建一个包含ChildProtocol 和Child_With_Value_Protocol both 的ParentProtocol 类型的单个数组。是否有任何可能的方法来创建一个循环遍历异构数组并返回仅类型为Child_With_Value_Protocol 的值的函数?
这可能需要架构更改。对所有解决方案开放。
尝试失败的解决方案 #1
var parents: [ParentProtocol] = [...both ChildProtocol & Child_With_Value_Protocol...]
func retrieveValues() -> [Any] {
var values = [Any]()
for parent in parents {
if let childWithValue = parent as? Child_With_Value_Protocol { // Fails to compile
values.append(childWithValue.retrieveValue())
}
}
return values
}
这会失败并出现 protocol 'Child_With_Value_Protocol' can only be used as a generic constraint because it has Self or associated type requirements 错误,这是有道理的,因为编译器在转换为 Child_With_Value_Protocol 时不知道类型,这会导致下一个失败的解决方案。
尝试失败的解决方案 #2
如果数组是一个只有Child_With_Value_Protocol 的同构数组,则可以使用类型擦除来检索值。
var parents: [ParentProtocol] = [...both ChildProtocol & Child_With_Value_Protocol...]
struct AnyValue {
init<T: Child_With_Value_Protocol>(_ protocol: T) {
_retrieveValue = protocol.retrieveValue as () -> Any
}
func retrieveValue() -> Any { return _retrieveValue() }
let _retrieveValue: () -> Any
}
func retrieveValues() -> [Any] {
var values = [Any]()
for parent in parents {
values.append(AnyValue(parent).retrieveValue()) // Fails to compile
}
return values
}
由于结构AnyValue 没有ParentProtocol 的初始化程序,因此编译失败。
尝试失败的解决方案 #3
struct AnyValue {
init<T: Child_With_Value_Protocol>(_ protocol: T) {
_retrieveValue = protocol.retrieveValue as () -> Any
}
func retrieveValue() -> Any { return _retrieveValue() }
let _retrieveValue: () -> Any
}
var erased: [AnyValue] = [AnyValue(...), AnyValue(...), AnyValue(...)]
func retrieveValues() -> [Any] {
var values = [Any]()
for value in erased {
values.append(value.retrieveValue())
}
return values
}
与其他解决方案不同,此解决方案实际上可以编译。这个解决方案的问题在于数组erased 只能保存Child_With_Value_Protocol 的类型擦除版本的值。目标是让数组同时保存 Child_With_Value_Protocol 和 ChildProtocol 的类型。
尝试失败的解决方案 #4
修改类型擦除结构以包含 ParentProtocol 的初始化程序仍会创建一个可编译的解决方案,但该结构将仅使用不太具体的 init,而不是更具体的 init。
struct AnyValue {
init?<T: ParentProtocol>(_ protocol: T) {
return nil
}
init?<T: Child_With_Value_Protocol>(_ protocol: T) {
_retrieveValue = protocol.retrieveValue as () -> Any
}
func retrieveValue() -> Any { return _retrieveValue() }
let _retrieveValue: (() -> Any)?
}
【问题讨论】:
-
这从一开始就注定要失败,这是不可能的,也没有任何意义。我相信您实际上甚至都不想要那个,也许您可以展示您的用例?
-
您甚至尝试这样做的事实意味着您不应该将所有这些元素放在一个数组中。
标签: ios swift generics swift-protocols protocol-oriented