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

让我们考虑一个引用视图模型的视图的简单示例,而不用考虑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()的调用,以设置委托.

解决Swift 3中缺少递归协议约束的问题的更多相关文章

  1. XCode 5远程调试OS X应用程序

    我正在使用XCode5.0.2在OSX10.9上开发一个应用程序并获得了一个我无法在这台开发机器上重现的错误报告.但是,我有一个10.7虚拟机出现崩溃,所以我想调试那里没有在这个VM中安装XCode.我搜索了有关远程调试的信息,但我没有得到有用的答案.我甚至担心它根本不受支持.但无论如何我还是要问一下.或者,除了执行完整的XCode安装等之外,还有哪些其他选项来调试这样的问题?

  2. ios – 嵌套递归函数

    我试图做一个嵌套递归函数,但是当我编译时,编译器崩溃.这是我的代码:编译器记录arehere解决方法有趣的…它似乎也许在尝试在定义之前捕获到内部的引用时,它是bailing?以下修复它为我们:当然没有嵌套,我们根本没有任何问题,例如以下工作完全如预期:我会说:报告!

  3. swift override --有一个递归问题未解决

    classca{varcount:Int{get{return1;}set{self.count=newValue;}}funcdescribe()->String{return"ca";}}classcb:ca{overridefuncdescribe()->String{return"cb";}overridevarcount:Int{get{return2;}set{//引起了递归调用,未找

  4. Swift2.0语言教程之函数嵌套调用形式

    Swift2.0语言教程之函数嵌套调用形式Swift2.0语言函数嵌套调用形式在Swift中,在函数中还可以调用函数,从而形成嵌套调用。以下将对这两种调用进行详细讲解。调用方式如图7.4所示。图7.4函数嵌套的形式以下将使用函数的嵌套调用实现对s=22!这个数值,即调用f1()函数,计算22,结果为4,然后在调用f2()函数,对4的阶乘求取,计算完成22!但是在Swift语言中递归必须要有一个满足结束的条件。

  5. cinder swift的区别

    [原]OpenStack入门以及一些资料之2014-4-29阅读1144评论0注:本文内容均来自网络,我只是在此做了一些摘抄和整理的工作,来源均有注明。它拥有自己的文件系统,通过网络文件系统NFS或通用文件系统CIFS对外提供文件访问服务。Raid,不同的raid等级在增加数据可靠性以及增加存储器(群)读写性能间取得平衡。卷组描述区域,和磁盘将包含分区信息的元数据保存在位于分区的起始位置的分区表中一样,逻辑卷以及卷组相关的元数据也是保存在位于物理卷的VGDA中。

  6. 【Swift】学习笔记(九)——枚举

    因为类完全可以替代枚举。不过swift中也有许多类的特性被枚举支持。这样判断必须穷举所有成员,否则就需要增加default这个选项了。使用递归枚举时,编译器会插入一个中间层。

  7. Swift实现的快速排序及sorted方法的对比

    Swift语言有着优秀的函数式编程能力,面试的时候面试官都喜欢问我们快速排序,那么用Swift如何实现一个快速排序呢?然后实现快速排序的方法:可以发现使用Swift实现快速排序的代码非常的简洁。在看完这段代码后我做了如下思考:既然是排序,那么必然可以使用系统的sorted方法,效果如何呢?对于快排最头疼的顺序性数组,sorted的重复次数只有n次!说明在面对这种类型的数组的时候sorted方法进行过判断,直接输出了。

  8. 《swift2.0 官方教程中文版》 第2章-08枚举

    importFoundation//在Swift中,枚举类型是一等公民。像Swift中其他类型一样,它们的名字必须以一个大写字母开头。给枚举类型起一个单数名字而不是复数名字,以便于读起来更加容易理解:vardirectionToHead=Compasspoint.West//directionToHead的类型可以在它被Compasspoint的一个可能值初始化时推断出来。//使用枚举成员的rawValue属性可以访问该枚举成员的原始值:letearthsOrder=Planet2.Earth.rawVa

  9. OpenStack中Swift和cinder区别

    swift是objectstorage,将object存储到bucket里,你可以用swift创建container,然后上传文件,例如视频,照片,这些文件会被replication到不同服务器上以保证可靠性,swift可以不依靠虚拟机工作。如果你把这个虚拟机terminate了,这个volume和里边的数据依然还在,你还可以把它接到其他虚拟机上继续使用里边的数据。cinder创建的volume必须被接到虚拟机上才能工作。

  10. swift枚举

    Swift中的枚举更加灵活,不必给每一个枚举成员提供一个值。它是Directionoperation类型,因为swift中的枚举不会自动给成员赋值为0,1…枚举类型易扩展。原始值的隐式赋值在使用原始值为整数或者字符串类型的枚举时,不需要显式地为每一个枚举成员设置原始值,Swift将会自动为你赋值。

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部