swift中协议之间的循环循环

时间:2014-10-05 18:23:00

标签: swift protocols

我试图在Swift文件中定义一些协议,但我注意到如果协议有交叉引用,XCode就会变得越来越糟糕,并且无法使用该项目。使用的协议的示例可能是下面使用的协议:

protocol VIPERPresenterProtocol
{
    var view: VIPERViewProtocol? { get set }
    var interactor: VIPERInteractorInputProtocol? { get set }
    var wireFrame: VIPERWireFrame? { get set }

    //    /* Add your extra communication methods here */
    //    /* Presenter -> ViewController */
}

protocol VIPERViewProtocol
{
    var presenter: VIPERPresenterProtocol? { get set }
}

VIPERPresenterProtocol引用了VIPERViewProtocol,最后一个引用了VIPERPresenterProtocol。

这是在Objective-C中有效的,但Swift并不喜欢。我的问题是,如果这可能是Swift语言的一个错误,或者我是否应该以任何其他方式实现这一点,这是Apple在Swift中不支持的东西。

2 个答案:

答案 0 :(得分:1)

您的问题有以下形式:

protocol A
{
    var b: B? { get set }
}

protocol B
{
    var a: A? { get set }
}

但是,虽然两个协议声明都是编译的,但任何实现它们的尝试都不会:

protocol A {
    var b: B? { get set }
}

protocol B {
    var a: A? { get set }
}

// on their own, the two protocol declerations compile

class Ca: A { // Does not compile: --> Segmentation fault 11
    var b: B?
}

如果我们打破这个循环,就不会发生这种情况:

protocol A {}

protocol B {
    var a: A? { get set }
}

class Ca: A {}

class Cb: B {
    var a: A?
}

let cb = Cb()   // --> {nil}
cb.a = Ca()     // --> {{Ca}}
cb.a            // --> {Ca}

或者,如果我们根据具体类型定义两个协议:

protocol A {
    var b: Cb? { get set }
}

protocol B {
    var a: Ca? { get set }
}

class Ca: A {
    var b: Cb?
    let i = "A"
}

class Cb: B {
    var a: Ca?
    let i = "B"
}

let cb = Cb()   // --> {nil "B"}
cb.a = Ca()     // --> {{{...}} "B"}
cb.a            // --> {{nil "A"}}
cb.a!.b = Cb()  // --> {{{...} "A"}}

我的印象是类型检查器还不能处理递归类型声明。不管它是否曾经,在这一点上甚至对苹果来说可能是一个悬而未决的问题,但是,由于明确意图促进功能习语,这至少是一种可能性。

之前我已回答similar question,因为我最终在@newacct的帮助下意识到,并不完全相同。

修改

我刚刚升级到 Xcode 6.1 GM Seed ,情况发生了变化!以下片段现在编译并且似乎运行正常!

protocol A {
    var b: B? { get set }
}

protocol B {
    var a: A? { get set }
}

class Ca: A {
    var b: B?
}

class Cb: B {
    var a: A?
}

let a = Ca()    // --> {nil}
let b = Cb()    // --> {nil}

a.b = b         // --> {{{...}}}
b.a = a         // --> {{{...}}}

(但是,此改进不会扩展到recursively defined associated types。)

答案 1 :(得分:0)

我试图为VIPER构建类似的东西,我遇到了同样的问题。

我在milos的好答案中添加了一些内容:即使使用相关类型,使用一些支持协议,我也成功地打破了循环。

protocol _VIPERViewProtocol {
}

protocol VIPERViewProtocol : _VIPERViewProtocol {
    typealias P:_VIPERPresenterProtocol
    var presenter: P! {get set}
}

protocol _VIPERPresenterProtocol {

}

protocol VIPERPresenterProtocol : _VIPERPresenterProtocol {
    typealias W:_VIPERViewProtocol
    var view: W! {get set}
}

这样做的好处是让编译器推断出关联的presenterview的正确类型,例如:

class BasePresenter : VIPERPresenterProtocol {
    var view : BaseView!
}

class BaseView : VIPERViewProtocol {
    var presenter: BasePresenter!
}

var p = BasePresenter()
// p.view is correctly recognized as BaseView!

只要您使用关联类型,此选项就有效。由于Swift中没有covariance属性,如果你改变它,上面的代码将无法编译:

protocol VIPERViewProtocol : _VIPERViewProtocol {
    var presenter: _VIPERPresenterProtocol! {get set}
}

无论如何,回到VIPER架构,这只会给具体类提供一个他们必须实现的模板。 如果我们可以定义一个“构建器”方法,使得通用对象符合协议(线框,演示者......),并连接所有组件,那将会更有用。

不幸的是,这样的事情不起作用:

func builder(p:VIPERPresenterProtocol, v:VIPERViewProtocol) {
    p.view = v
    v.presenter = p
}

编译器抱怨Protocol 'VIPERViewProtocol' can only be used as a generic constraint because it has Self or associated type requirements

也许一个可以探索的解决方案是泛型,但我仍然需要考虑它。