在 Swift 中使用协议作为类型时出错
Posted
技术标签:
【中文标题】在 Swift 中使用协议作为类型时出错【英文标题】:Error When Using Protocol as a Type in Swift 【发布时间】:2016-01-19 17:50:01 【问题描述】:我正在使用 Swift 并尝试制作一些集合对象。这些集合对象有一个支持Dictionary
来保存自定义对象。例如,一个对象的类型可能是Cat
,而集合对象的类型可能是Cats
。 Cats
将有一个包含 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
我四处搜索,看起来我的Key
typealias
遵循的Hashable
协议导致了这种情况。解决这个问题的最佳方法是什么?有没有办法更改协议以使我不需要Hashable
,或者我是否需要在使用ObjectDictionaryProtocol
的类中做一些事情?或者也许有更好的方法来有效地“子类化”Swift Dictionary
(引用是因为我意识到 Dictionary
struct
不能被子类化)?
【问题讨论】:
实际上,如果你把所有的东西都去掉,你会发现仅仅在你的协议中使用一个类型别名就禁止你使用协议作为一种类型,正如错误消息中所解释的那样:"。 .. 或相关的类型要求”。如果包含类型别名,则包含关联类型。我建议您使用符合您协议的自定义结构。 【参考方案1】:具有关联类型的协议与其说是一种类型,不如说是一种类型的模板。当指定协议的关联类型时,实际类型存在。因此,ObjectDictionaryProtocol
在您指定时“成为”一种类型:
ObjectDictionaryProtocol<String,Cat>
但上面的 Swift 无效...
所以你问...'[是否有]一种更好的方法来有效地'子类'一个 Swift 字典'。你可能会得到一个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> = ...
// ...
【讨论】:
【参考方案2】:原因是因为当您使用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<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
是)
【讨论】:
以上是关于在 Swift 中使用协议作为类型时出错的主要内容,如果未能解决你的问题,请参考以下文章
Swift 2在协议扩展中使用变异函数时出错“无法在不可变值上使用变异成员:'self'是不可变的
Apollo graphQL:在 iOS swift 中使用自定义标量时解码数组时出错