在通用函数中使用协议的关联类型

时间:2018-11-27 20:09:10

标签: swift swift-protocols

我正在尝试编写一个简单的MVP模式以遵循我的应用程序,因此我编写了两个porotocol来定义View Controller和Presenters:

protocol PresenterType: class {
    associatedtype ViewController: ViewControllerType

    var viewController: ViewController? { get set }

    func bind(viewController: ViewController?)
}

protocol ViewControllerType: class {
    associatedtype Presenter: PresenterType

    var presenter: Presenter { get }

    init(presenter: Presenter)
}

在定义了这些内容之后,我开始写一些RootViewControllerRootViewPresenter。后者看起来像:

protocol RootViewControllerType: ViewControllerType {
}

final class RootPresenter<VC: RootViewControllerType>: PresenterType {
    weak var viewController: VC?

    func bind(viewController: VC?) {
        self.viewController = viewController
    }
}

到目前为止,所有内容都符合并且很好,但是当我开始像这样实现View Controller时:

protocol RootPresenterType: PresenterType {
}

final class RootViewController<P: RootPresenterType>: UIViewController, ViewControllerType {
    let presenter: P

    init(presenter: Presenter) {
        self.presenter = presenter
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        presenter.bind(viewController: self)
    }
}

我立即收到以下错误消息:

  

无法将类型'RootViewController

'的值转换为预期的参数类型'_?'

我知道具有关联类型的protocol可能会带来一些限制,但是此示例非常简单,我无法使其正常工作。是否有可能实现我想要的目标,还是必须寻找其他一些不那么迅速的模式?

1 个答案:

答案 0 :(得分:2)

由于PresenterTypeViewControllerType协议各自相关类型之间的循环依赖性,我认为您要实现的目标是不可能的。

请考虑一下,如果 did 以上的可疑代码编译了……您将如何实例化RootPresenterRootViewController类?因为两者相互依赖,所以最终会出现如下错误:

Generic parameter error 1. Generic parameter error 2.

如您所见,由于类型相关,编译器无法完全解析通用参数。

我认为您最好的选择是从一个或两个协议中删除关联的类型。例如,从PresenterType协议中删除关联的类型并更新RootPresenter类将打破循环依赖关系,并允许您的代码正常编译。

protocol PresenterType: class {
    var viewController: UIViewController? { get set }

    func bind(viewController: UIViewController?)
}

final class RootPresenter: PresenterType {
    weak var viewController: UIViewController?

    func bind(viewController: UIViewController?) {
        self.viewController = viewController
    }
}