使用协议和关联类型的强类型树层次结构

时间:2017-09-07 09:59:21

标签: swift generics protocols

我试图使用协议设计一个强类型的对象层次结构,但不能完全正确。

为了说明,假设最终采用这些协议的具体类型是 CountryStateCity

每个节点都可以有一个父节点(根对象除外)和/或子节点(叶子对象除外):所有State个实例都是单个Country实例的子节点,并将其作为父节点,并且他们有City个实例作为孩子等。

所以我从这两个协议开始:

/// To be adopted by State and City
///
protocol Child: AnyObject {
    associatedtype ParentType: AnyObject 
    // (-> so that property `parent` can be weak)

    weak var parent: ParentType? { get }
} 

/// To be adopted by Country and State
///
protocol Parent: AnyObject {
    associatedtype ChildType: AnyObject
    // (-> for symmetry)

    var children: [ChildType] { get }
}

我有两个单独的协议,而不是将所有上述要求分组的协议,因为我不想指定"虚拟" typealias ParentType用于根类Country(没有意义),也不是"虚拟"}叶类typealias ChildType的{​​{1}}。

相反,我可以将父行为和子行为分开,只让中间类City采用两种协议。

下一步,我想从磁盘读取的字典中初始化我的clases。对于子类,我想在实例化时指定父类,所以我提出了这个方案:

State

就目前而言,看起来我可以更进一步,并添加方法protocol UnarchivableChild: Child { init(dictionary: [String: Any], parent: ParentType?) } protocol UnarchivingParent: Parent { associatedtype ChildType: UnarchivableChild func readChildren(fromDictionaries dictionaries: [[String: Any]]) -> [ChildType] } 的默认实现,如下所示:

readChildren(fromDictionaries:)

...因为在此协议中,extension UnarchivingParent { func readChildren(fromDictionaries dictionaries: [[String: Any]]) -> [ChildType] { return dictionaries.flatMap({ dictionary in return ChildType(dictionary: dictionary, parent: self) }) } } 被约束为ChildType,所以它应该支持初始化程序......?但我明白了:

  

无法调用' ChildType'使用类型'的参数列表(字典:([String:Any]),parent:Self)'

(为何大写"自我"?)

我想我错过了关于相关类型如何工作的内容......

如何对此默认实施进行编码?

更新:显然,以某种方式传递自我是个问题。我将代码修改为:

UnarchivableChild

错误是:

  

无法分配类型' Self'输入' _?'

1 个答案:

答案 0 :(得分:0)

阅读The Swift Programmng Language: Generics后的章节:" Generci Where Clause&#34的扩展;),我找到了合适的语法来实现我想要的。我决定使用这个代码(类型名称与原始问题中的名称略有不同):

protocol DictionaryInitializable {
    init(dictionary: [String: Any])
}

protocol ChildNode: AnyObject {
    associatedtype ParentType: AnyObject

    weak var parent: ParentType? { get set }
}

protocol ParentNode {
    associatedtype ChildType: AnyObject

    var children: [ChildType] { get set }
}

// This 'WHERE' clause fixes the issue:
extension ParentNode where ChildType: DictionaryInitializable,
ChildType: ChildNode, ChildType.ParentType == Self {

    func readChildren(from dictionaries: [[String: Any]]) -> [ChildType] {
        return dictionaries.map({
            return ChildType(dictionary: $0)
        })
    }
}

where子句的第一个约束允许我调用初始值设定项init(dictionary:),第二个约束保证属性parent的存在,第三个约束允许我指定self作为其值。

或者,我可以改变:

protocol ParentNode {
    associatedtype ChildType: AnyObject

为:

protocol ParentNode {
    associatedtype ChildType: ChildNode

...并跳过第二个约束。