为什么我必须在Swift类上放弃协议一致性以进行数组扩展?

时间:2019-04-08 09:11:20

标签: swift

我正在实现一个树模型。每种节点类型都可以选择多种功能。我正在使用基础class Node {}来实现此功能,然后使用默认实现的许多协议来描述这些功能。然后,我有了从Node派生的并符合协议列表的最终类来描述我的结构。

每个协议具有以下样式的关联代码:

protocol Protocol {}

typealias ProtocolConformingNode = Node & Protocol

extension Node
{
    public var isProtocolConforming: Bool {
        return asProtocolConforming != nil
    }

    public var asProtocolConforming: ProtocolConformingNode? {
        return self as? ProtocolConformingNode
    }
}

我使用Node & Protocol,以便可以对返回的项目使用其他与协议相关的查询,例如asOtherProtocolConforming

一切正常,直到我处理ProtocolConformingNode的数组为止。最初,我在过滤节点+协议数组时遇到问题,我想在过滤后保持一致性:

extension Array where Element: Node
{
    var someFilter: [Element] {
        return self
    }
}

let array = [ProtocolConformingNode]()
let filteredArray = array.someFilter

这会出现以下错误:

[ProtocolConformingNode]' (aka 'Array<Node & Protocol>') requires that 'ProtocolConformingNode' conform to 'AnyObject'

感觉有点奇怪,但是我认为它属于之前在SO上讨论过的一个问题:Protocol doesn't conform to itself?

我认为,对于我使用的这种罕见情况,所有协议的类型擦除都太多了,因此我对过滤后的节点转换感到满意。请注意,在以下代码中我没有使用Element,而是显式使用了NodeElement == Node

extension Array where Element == Node
{
    var someFilter: [Node] {
        return self
    }
}

let array = [ProtocolConformingNode]()
let filteredArray = array.someFilter as! [ProtocolConformingNode]

现在出现错误:

'[ProtocolConformingNode]' (aka 'Array<Node & Protocol>') is not convertible to 'Array<Node>'

对我来说很奇怪。我可以使它像这样工作:

let filteredArray = (array as [Node]).someFilter as! [ProtocolConformingNode]

但是为什么我必须在这里投?以下内容如何运作?

var nodes = [Node]()
var protocolConformingNodes = [ProtocolConformingNode]()
nodes.append(contentsOf: protocolConformingNodes)

何时:

mutating func append<S>(contentsOf newElements: S) where Element == S.Element, S : Sequence

1 个答案:

答案 0 :(得分:0)

这是与您链接的SO帖子相同的问题。特别是,您应该阅读Hamish的答案。 IMO,这是该语言的缺点,只有在Protocol具有静态或初始化要求的情况下才是问题,而在这种情况下则没有。

快速而肮脏的解决方法是使用@objc。完整的代码看起来像

class Node {}

@objc protocol Protocol {}

typealias ProtocolConformingNode = Node & Protocol

extension Array where Element: Node
{
    var someFilter: [Element] {
        return self
    }
}

let array = [ProtocolConformingNode]()
let filteredArray = array.someFilter // Yay! No error this time.