解决Swift 3中缺少递归协议约束的问题

Swift 3(Xcode 8 beta 6)目前对“递归协议约束”有一个限制.有一个公开的问题 here,在 here,herehere有类似的讨论.但是,我仍然没有看到应该如何解决这个限制.可能吗?

让我们考虑一个引用视图模型的视图的简单示例,而不用考虑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
}

使用上述代码,您将遇到“类型”可能不会将其自身参考为所提供链接中所讨论的要求.

然后(天真的,因为我),我以为我可以解决这个做:

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实现中设置.如果尝试这样做,错误无法分配View< VM>类型的值输入 _?会出现.

然而,通过这种方法,我们可以得到正确的类型,而无需做很多专业化.当然,可以添加更多的协议来获得更多的抽象,但同样的解决方案将会适用.

由于某些原因/语言缺陷,您必须在View.foo中分配委托时使用显式转换:viewModel.delegate = self as? VM.D

但是为什么要使用结构体而不是类?我想你想要类,特别是你不希望所有的View / ViewModel变量在修改时复制 – 而不是被引用 – 当你做类似的事情

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

特别

func foo() {
    viewModel.delegate = self
}

不起作用,除非你声明View.foo是变异的,这将需要几乎所有的东西(包括ViewDelegate.foo和ViewModel.bar)进行突变,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>

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

// initializer needed,because we are no struct any more
init(viewModel vm:VM) {
    self.viewModel = vm
    self.viewModel.delegate = self as? VM.D
}

然后,您可以跳过v.foo()的调用,以设置委托.

相关文章

软件简介:蓝湖辅助工具,减少移动端开发中控件属性的复制和粘...
现实生活中,我们听到的声音都是时间连续的,我们称为这种信...
前言最近在B站上看到一个漂亮的仙女姐姐跳舞视频,循环看了亿...
【Android App】实战项目之仿抖音的短视频分享App(附源码和...
前言这一篇博客应该是我花时间最多的一次了,从2022年1月底至...
因为我既对接过session、cookie,也对接过JWT,今年因为工作...