解决Swift 3

时间:2016-08-18 08:08:01

标签: swift swift3

Swift 3(Xcode 8 beta 6)目前在“递归协议约束”方面存在限制。有一个未解决的问题herehereherehere也有类似的讨论。但是,我仍然没有看到应该如何解决这个限制。有可能吗?

让我们考虑引用视图模型的视图的简单示例,反之,不要考虑ref / value类型以及任何保留周期:

protocol ViewModelType {
    associatedtype V: ViewType
    var view: V { get }
}

struct ViewModel<V: ViewType>: ViewModelType {
    var view: V
}


protocol ViewType {
    associatedtype VM: ViewModelType
    var viewModel: VM { get }
}

struct View<VM: ViewModelType>: ViewType {
    var viewModel: VM
}

使用上面的代码,您将遇到提供的链接中讨论的Type may not reference itself as a requirement

然后(我很天真),我以为我可以通过这样做来解决这个问题:

protocol _ViewModelType {}
protocol ViewModelType: _ViewModelType {
    associatedtype V: _ViewType
    var view: V { get }
}

struct ViewModel<V: ViewType>: ViewModelType {
    var view: V
}


protocol _ViewType {}
protocol ViewType: _ViewType {
    associatedtype VM: _ViewModelType
    var viewModel: VM { get }
}

struct View<VM: ViewModelType>: ViewType {
    var viewModel: VM
}

这会导致错误,但它基本上只是推迟了问题。因为现在当我们想构建我们的具体类型时,我们最终会进入一个永无止境的专业化循环:

let vm = ViewModel<View<ViewModel<View...>>>()

我认为这是我想在协议中加入的一个基本约束,但目前我没有看到如何做到这一点。在Swift更新之前,是否可以解决这个问题?或者我需要开始引入不太严格的协议,直到在Swift中实现它?

2016年8月19日更新

经过多方努力找出解决这个问题的最佳方法后,我相信我找到了一个可以接受的解决方案,只需要很少的妥协:

protocol ViewModelType {
    associatedtype D: Any // Compromise to avoid the circular protocol constraints.
    var delegate: D? { get set }
}

protocol ViewType {
    associatedtype VM: ViewModelType
    var viewModel: VM { get }
}

protocol ViewDelegate {
    func foo()
}


struct ViewModel: ViewModelType {
    typealias D = ViewDelegate
    var delegate: D?

    func bar() {
        delegate?.foo() // Access to delegate methods
    }
}

struct View<VM: ViewModelType>: ViewType, ViewDelegate {
    var viewModel: VM

    func foo() {
        // Preferred, but not possible: viewModel.delegate = self
    }
}


var vm = ViewModel() // Type: ViewModel
let v = View(viewModel: vm) // Type: View<ViewModel>
vm.delegate = v

主要思想是,引入中介对象或委托对象,以处理视图和视图模型之间的通信。对该委托的引用是Any类型,以打破循环协议约束。我认为唯一的缺点是委托需要“从外部”设置,从中创建对象,并且不能在View实现中设置。如果尝试执行此操作,则会显示错误Cannot assign value of type View<VM> to type _?

但是,通过这种方法,我们可以获得正确的类型,而无需进行大量的专业化。当然,可以添加更多协议以获得更多抽象,但同样的解决方案也适用。

1 个答案:

答案 0 :(得分:1)

由于某些原因/语言缺陷,您必须在iss: www.amazon.com aud: App Id sub: Sub from the LWA token. 中分配代理时使用显式转换:View.foo

但为什么你要使用结构而不是类?我认为你想要课程,尤其是你不希望所有那些View / ViewModel变量在修改时被复制 - 而不是被引用 - 当你做类似的事情时

viewModel.delegate = self as? VM.D

特别是

var vm = ViewModel() // Type: ViewModel
let v = View(viewModel: vm) // Type: View<ViewModel> 
vm.delegate = v
除非您将func foo() { viewModel.delegate = self } 声明为View.foo,否则

无法工作,这将需要制作几乎所有内容(包括mutatingViewDelegate.foo){{ 1}}和ViewModel.bar不能再是常数了。

虽然下面的解决方案也适用于结构体,但我只是改变了类:

mutating

还有一件事:为什么不在视图类的初始化程序中分配委托,例如:

v = View(viewModel: vm)

然后,您可以跳过调用protocol ViewModelType { associatedtype D: Any // Compromise to avoid the circular protocol constraints. var delegate: D? { get set } } protocol ViewType { associatedtype VM: ViewModelType var viewModel: VM { get } } protocol ViewDelegate { func foo() } class ViewModel: ViewModelType { typealias D = ViewDelegate var delegate: D? func bar() { delegate?.foo() // Access to delegate methods } } class View<VM: ViewModelType>: ViewType, ViewDelegate { var viewModel: VM // initializer needed, because we are no struct any more init(viewModel vm:VM) { self.viewModel = vm } func foo() { viewModel.delegate = self as? VM.D } } var vm = ViewModel() // Type: ViewModel let v = View(viewModel: vm) // Type: View<ViewModel> v.foo() // View<ViewModel> vm.delegate // View<ViewModel> 以设置代理。