Swift 4协议组成一致性

时间:2017-10-26 13:05:38

标签: swift swift4

[首先,如果有人对问题有更好的名称,提案就会被广泛接受。到目前为止,我还没有找到更好的名称。]

那就是问题所在。

假设我有一个协议ProtocolA,协议ProtocolB和协议ProtocolX定义如下:

protocol ProtocolA {
  func doSomethingA()
}

protocol ProtocolB {
   func doSomethingB()
}

protocol ProtocolX {
   var handler: ProtocolA { get }
}

然后我在ProtocolX的一个类中有一个正确的实现如下:

class Donald: ProtocolX {
   ...
   var handler: ProtocolA { ... }
   ...
}

然后一切正常,并且正确履行了协议要求。

BUT

如果我这样实施:

class Donald: ProtocolX {
   ...
   var handler: ProtocolA & ProtocolB
   ...
}

我有一个编译时问题报告我的班级Donald不符合ProtocolX(根据规范,要求var handler必须符合{{1} }})。

理论上 var ProtocolA IS符合handler(但它也符合ProtocolA)。

然后我的问题是:为什么编译时问题?我方面的迅速限制或概念问题?

3 个答案:

答案 0 :(得分:1)

没有充分的理由说明它不起作用,只是编译器尚不支持。

当前,ProtocolX指定处理程序必须为ProtocolA类型,这意味着如果您为类Donald声明了ProtocolA类型以外的其他内容,则该处理程序将无法实现协议的要求。

但是,您可以将处理程序指定为ProtocolA类型,但将其设置为ProtocolA & ProtocolB类型的属性。在这种情况下,该属性将强制转换为ProtocolA,并且您需要进行其他强制转换才能使用ProtocolB的属性。例如:

typealias ProtocolAB = ProtocolA & ProtocolB

class AB: ProtocolAB {
    func doSomethingA() {}
    func doSomethingB() {}
}

class Donald: ProtocolX {
    var handler: ProtocolA {
        return AB()
    }

    func f() {
        handler.doSomethingA() // is valid
        handler.doSomethingB() // is not valid without casting the handler as `ProtocolB`
    }
}

答案 1 :(得分:0)

您可能可以通过扩展其中一种协议来实现另一种协议来解决该问题,从而解决该问题。

您可以使A从B继承:

protocol ProtocolA: ProtocolB {
  func doSomethingA()
}

protocol ProtocolB {
   func doSomethingB()
}

protocol ProtocolX {
   var handler: ProtocolA { get }
}

由于A继承自B,所以B中的每个属性在A中都可用。

然后您可以在协议X中同时使用它们,因为A继承自b

选中此answer

答案 2 :(得分:0)

不幸的是,至少当前要求协议中指定的类型是准确的。但是,目前您可以执行一些解决方法。

最简单的选择是将handler保留为ProtocolA,但实际上存储一个ProtocolA & ProtocolB对象。只要您不需要遵循也有var handler: ProtocolB的协议,这是可行的。

class Donald: ProtocolX {
    var abHandler: ProtocolA & ProtocolB
    var handler: ProtocolA { return abHandler }
}

如果您遇到的一个更复杂的情况是您实际上需要 handler既是ProtocolA又是ProtocolB,则您必须在协议:

protocol ProtocolX {
    associatedtype Handler: ProtocolA
    var handler: Handler { get }
}
protocol ProtocolY {
    associatedtype Handler: ProtocolB
    var handler: Handler { get }
}

关联类型的问题是您需要为它们指定一个具体类型,因此即使ProtocolA & ProtocolB是变量的有效类型,也不能将其用作关联类型Handler。因此,您要么需要将特定的具体类型设为Handler

struct PrintAB: ProtocolA, ProtocolB {
    func doSomethingA() { print("A") }
    func doSomethingB() { print("B") }
}
class Donald: ProtocolX, ProtocolY {
    var handler: PrintAB
}

…或者您可以使Donald本身是通用的:

class Donald<Handler>: ProtocolX, ProtocolY where Handler: ProtocolA & ProtocolB {
    init(handler: Handler) { self.handler = handler }
    var handler: Handler
}

尽管这两种方法都可以使用,但是您也将失去使用ProtocolX作为变量类型的能力,即let someX: ProtocolX = donald由于{{1 }}。

当然,如果您可以控制协议定义,则可以通过为处理程序使用不同的名称来避免这种情况,将问题减少到更简单的情况:

ProtocolX