首页 > 解决方案 > 为什么我必须放弃 Swift 类的协议一致性以进行数组扩展?

问题描述

我正在实现一个树模型。每种节点类型都可以选择多种功能。我用一个 base 来实现它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 上讨论过的一个问题:协议不符合自身?

我决定对于我使用它的极少数情况来说,类型擦除我的所有协议会太多,所以我会满足于在过滤后转换我的节点。请注意,我没有Element在以下代码中使用,而是明确使用Node, 和Element == 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

标签: swift

解决方案


这与您链接的 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.

推荐阅读