我已经发布了question,但我不清楚我想要什么。正如@AlainT建议的那样,我提出了一个新的。
我有一个typealias元组
public typealias MyTuple<T> = (key: T, value: String)
协议:
public protocol VCADelegate: class {
associatedtype T
func didSelectData(_ selectedData: MyTuple<T>)
}
具有表格视图的视图控制器(VCA)
class VCA<T>: UIViewController, UITableViewDelegate, UITableViewDataSource {
var dataList = [MyTuple<T>]()
weak var delegate: VCADelegate? // Error: can only be used as a generic constraint
// ...
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
delegate?.didSelectData(dataList[indexPath.row])
}
}
在另一个视图控制器(VCB)中,我创建了一个VCA并传递了一个dataList
func callVCA() {
let vcA = VCA<String>()
vcA.dataList = [(key: "1", value:"Value 1"),
(key: "2", value:"Value 2")]
}
我想要做的是在不知道VCA中的密钥数据类型的情况下拥有dataList。只有当VCB调用VCA时,我才知道密钥的数据类型。创建通用视图控制器将导致委托问题。有什么方法可以解决这个问题,而不必改为关闭完成?
使用通用视图控制器的另一个问题是我无法扩展它。有什么想法吗?
答案 0 :(得分:3)
这是一种标准的类型擦除情况,但在这种特殊情况下,我只是传递一个闭包(因为只有一种方法)。
创建类型橡皮擦而不是协议:
public struct AnyVCADelegate<T> {
let _didSelectData: (MyTuple<T>) -> Void
func didSelectData(_ selectedData: MyTuple<T>) { _didSelectData(selectedData)}
init<Delegate: VCADelegate>(delegate: Delegate) where Delegate.T == T {
_didSelectData = delegate.didSelectData
}
}
使用它代替委托:
class VCA<T>: UIViewController, UITableViewDataSource UITableViewDelegate {
var dataList = [MyTuple<T>]()
var delegate: AnyVCADelegate<T>?
// ...
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
delegate?.didSelectData(dataList[indexPath.row])
}
}
您的根本问题是具有关联类型的协议本身并不是合适的类型。他们只是键入约束。如果你想保留PAT,那很好,但是你必须通过代表使VCA
通用:
class VCA<Delegate: VCADelegate>: UIViewController, UITableViewDelegate {
var dataList = [MyTuple<Delegate.T>]()
weak var delegate: Delegate?
init(delegate: Delegate?) {
self.delegate = delegate
super.init(nibName: nil, bundle: nil)
}
required init(coder: NSCoder) { super.init(nibName: nil, bundle: nil) }
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
delegate?.didSelectData(dataList[indexPath.row])
}
}
class VCB: UIViewController, VCADelegate {
func didSelectData(_ selectedData: MyTuple<String>) {}
func callVCA() {
let vcA = VCA(delegate: self)
vcA.dataList = [(key: "1", value:"Cinnamon"),
(key: "2", value:"Cloves")]
}
}
通常,具有关联类型(PAT)的协议是一种非常强大但特殊的工具。它们不是泛型(它是一种通用工具)的替代品。
对于这个特殊问题,我可能只是通过一个闭包。所有类型的橡皮擦(通常)都是填充有封闭物的结构。 (有一天,编译器可能只会为我们编写它们,并且这个问题的大部分将会消失,PAT将在日常代码中有用,但是现在它并没有。)