【问题标题】:Strongly Typed Tree Hierarchy Using Protocols and Associated Types使用协议和相关类型的强类型树层次结构
【发布时间】:2017-09-07 09:59:21
【问题描述】:

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

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

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

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

/// 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 }
}

我有两个单独的协议,而不是一个组合所有上述要求的协议,因为我不想为根类 Country 指定一个“虚拟”typealias ParentType(这是没有意义的),也不是叶类City的“虚拟”typealias ChildType

取而代之,我可以将父子行为分开,只让中间类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 被限制为UnarchivableChild,所以它应该支持初始化器...?但我明白了:

无法使用类型为“(字典:([String : Any]),父级:Self)”的参数列表调用“ChildType”

(为什么是大写的“Self”?)

我想我遗漏了一些关于关联类型如何工作的东西......

如何编码这个默认实现?


更新:显然,传递 self 是个问题。我将代码修改为:

protocol Node: AnyObject {
}

protocol Parent: Node {
    associatedtype ChildNodeType: Node
    var children: [ChildNodeType] { get set }
}

protocol Child: Node {
    associatedtype ParentNodeType: Node
    weak var parent: ParentNodeType? { get set }
}

protocol UnarchivableChild: Child {
    init(dictionary: [String: Any]) // Removed parent parameter!
}

protocol UnarchivingParent: Parent {
    associatedtype ChildNodeType: UnarchivableChild
    func readChildren(fromDictionaries dictionaries: [[String: Any]]) -> [ChildNodeType]
}

extension UnarchivingParent {
    func readChildren(fromDictionaries dictionaries: [[String: Any]]) -> [ChildNodeType] {
        return dictionaries.flatMap({
            let child = ChildNodeType(dictionary: $0)
            // Assign parent here instead:
            child.parent = self // < ERROR HERE

            return child
        })
    }
} 

错误是:

无法将类型“Self”的值分配给类型“_?”

【问题讨论】:

    标签: swift generics protocols


    【解决方案1】:

    在阅读了The Swift Programmng Language: Generics(章节:“使用 Generci Where 子句的扩展”)之后,我找到了实现我想要的适当语法。我选择了这个代码(类型名称与原始问题中的名称略有不同):

    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
    

    ...跳过第二个约束。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-29
      • 2016-09-14
      • 2013-03-20
      • 2020-08-22
      相关资源
      最近更新 更多