通用视图控制器不适用于委托和扩展

Law*_*iet 2 generics delegates ios type-alias swift

我已经发布了一个问题但不清楚我想要什么。正如@AlainT 所建议的,我提交了一份新文件。

我有一个 typealias 元组

public typealias MyTuple<T> = (key: T, value: String)
Run Code Online (Sandbox Code Playgroud)

一个协议:

public protocol VCADelegate: class {
    associatedtype T
    func didSelectData(_ selectedData: MyTuple<T>)
}
Run Code Online (Sandbox Code Playgroud)

带有表视图的视图控制器 (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])
    }
}
Run Code Online (Sandbox Code Playgroud)

在另一个视图控制器 (VCB) 中,我创建了一个 VCA 并传递了一个 dataList

func callVCA() {
    let vcA = VCA<String>()
    vcA.dataList = [(key: "1", value:"Value 1"),
                    (key: "2", value:"Value 2")]
} 
Run Code Online (Sandbox Code Playgroud)

我想要做的是在不知道 VCA 中密钥的数据类型的情况下拥有一个 dataList。只有当 VCB 调用 VCA 时,我才知道密钥的数据类型。创建通用视图控制器会导致委托出现问题。有什么方法可以解决这个问题而不必更改为关闭完成?

使用通用视图控制器的另一个问题是我无法扩展它。任何的想法?

Rob*_*ier 5

这是一种标准的类型擦除情况,但在这种特殊情况下,我只会传递一个闭包(因为只有一种方法)。

创建一个类型橡皮擦而不是协议:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

使用它而不是委托:

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])
    }
}
Run Code Online (Sandbox Code Playgroud)

您的潜在问题是具有关联类型的协议本身并不是正确的类型。它们只是类型约束。如果您想将其保留为 PAT,那很好,但是您必须VCA在 Delegate上进行泛型化:

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")]
    }
}
Run Code Online (Sandbox Code Playgroud)

通常,具有关联类型 (PAT) 的协议是一种非常强大但具有特殊用途的工具。它们不是泛型(通用工具)的替代品。

不过,对于这个特定的问题,我可能只是通过了一个闭包。所有类型擦除器(通常)都是一个填充了闭包的结构。(总有一天编译器可能会为我们编写它们,这个问题的大部分都会消失,PAT 将在日常代码中很有用,但现在还没有。)