在Swift中使用协议作为类型时出错

时间:2016-01-19 17:50:01

标签: ios swift generics dictionary protocols

我正在使用Swift并尝试制作一些集合对象。这些集合对象具有支持Dictionary以保存自定义对象。例如,对象可能是Cat类型,集合对象的类型为CatsCats将包含一个包含Cat类型值的私有字典。我有其他类型也需要相应的集合(每个集合类型具有它所拥有的类型的特定逻辑)。

我创建了一个协议,以确保每个集合都有一些共同的特征。这些常用功能和下标通常是支持字典的直通。这是协议:

protocol ObjectDictionaryProtocol {

    // These are necessary for generics to work in protocols
    typealias Key: Hashable
    typealias Value


    // MARK: - Properties

    var count: Int { get }
    var isEmpty: Bool { get }
    var keys: LazyMapCollection<Dictionary<Key, Value>, Key> { get }
    var values: LazyMapCollection<Dictionary<Key, Value>, Value> { get }


    // MARK: - Subscripts

    subscript(key: Key) -> Value? { get set }

}

当我真正将协议用作类型时,例如:

var objects: ObjectDictionaryProtocol

init(objs: ObjectDictionaryProtocol) {
    ...
}

我收到错误:

Protocol 'ObjectDictionaryProtocol' can only be used as a generic constraint because it has Self or associated type requirements

我已经四处搜索,看起来我Hashable Key符合的typealias协议正在导致此问题。解决这个问题的最佳方法是什么?有没有办法更改协议,以便我不需要Hashable,或者我是否需要在使用ObjectDictionaryProtocol的类中执行某些操作?或者也许有一种更好的方法可以有效地进行子类化&#39;一个Swift Dictionary(引用,因为我意识到Dictionary struct不能被子类化?)

2 个答案:

答案 0 :(得分:0)

具有关联类型的协议不是类型,而是类型的模板。当指定协议的关联类型时,存在实际类型。因此ObjectDictionaryProtocol&#39;变为&#39;指定时的类型:

ObjectDictionaryProtocol<String,Cat>

但上述内容无效Swift ......

所以你问过......&#39; [有]更好的方法来有效地分类&#39;一个快速字典&#39;。您可以使用extension获得类似的内容:

class Cat {}

extension Dictionary where Value : Cat {
  func voice (name: Key) {
    if let _ = self[name] {
      print ("\(name): Meow")
    }
  }
}

var someCats = Dictionary<String,Cat>()
someCats["Spot"] = Cat()
someCats.voice("Spot")
// Spot: Meow

或者您可能需要实际执行该协议。

class ObjectDiciontary<Key:Hashable, Value> : ObjectDictionaryProtocol {
  var backingDictionary = Dictionary<Key,Value>()
  // implement protocol
}

var object : ObjectDictionary<String,Cat> = ...
// ...

答案 1 :(得分:0)

原因是当您使用typealias时,您有效地将Protocol作为Protocol<T>的通用协议。请耐心等待我,我将解释为什么以及如何解决它。

这里的问题是Apple决定使用typealias关键字来定义关联类型。这在Swift 2.2(Xcode 7.3)中得到修复 您可以在https://github.com/apple/swift-evolution/blob/master/proposals/0011-replace-typealias-associated.md

上详细了解相关信息

它正在重命名为associatedtype,这更有意义。

这意味着必须采用您的协议,并定义其相关类型。

在你的情况下,它看起来像

protocol ObjectDictionaryProtocol {
  associatedtype Value
}

extension String : ObjectDictionaryProtocol {
  associatedtype = Double
}

init可能看起来像

 init<T : ObjectDictionaryProtocol>(objs: ObjectDictionaryProtocol)

init<T : ObjectDictionaryProtocol 
     where T.Value == Double>(objs: ObjectDictionaryProtocol)

现在typealias Key: Hashable表示分配给Key的任何类型都必须符合Hashable

这会给您一个String不符合ObjectDictionaryProtocol的错误,因为它无法满足要求。

protocol ObjectDictionaryProtocol {
  associatedtype Value : FloatingPointType
}

extension String : ObjectDictionaryProtocol {
  associatedtype = Int
}

Int不是FloatingPointType(但Double