考虑这个例子:
protocol Observable: Hashable {
// ...
}
struct People: Observable {
var name: String
var age: Double
var hashValue: Int {
// ...
}
static func ==(lhs: People, rhs: People) -> Bool {
// ,,,
}
}
struct Color: Observable {
var red: Double, green: Double, blue: Double
var hashValue: Int {
// ...
}
static func ==(lhs: Color, rhs: Color) -> Bool {
// ...
}
}
var observers: Set<Observable> = [] // Not allowed by the compiler
People和Color都符合Observable
协议,该协议也继承自Hashable
协议。我想将这些内容存储在observers
集。
using 'Observable' as a concrete type conforming to protocol
'Hashable' is not supported
是否有可能在Swift中做异类Set?
答案 0 :(得分:2)
有一种方法可以实现。(受Apple的实施启发)
在开始之前,这就是我们想要构建的内容。
protocol Observer: Hashable {
associatedtype Sender: Observable
func valueDidChangeInSender(_ sender: Sender, keypath: String, newValue: Any)
}
此问题的根源是使用强制数组为同源的Self
。你可以在这里看到它:
最重要的变化是它阻止协议作为一种类型使用。
这使我们无法做到:
var observers: [Observer] = [] // Observer is not usable as a type.
因此,我们需要另一种方法来使其发挥作用。
我们不做
var observers: [AnyHashable] = []
因为AnyHashable
不会将对象约束为符合Observer
协议。相反,我们可以将Observer
对象包装在AnyObserver
包装器中,如下所示:
var observers: [AnyObserver] = []
observers.append(AnyObserver(yourObject))
这将确保AnyObserver
struct的值符合Observer
协议。
根据WWDC 2015:Swift中面向协议的编程,我们可以使用isEqual(_:)
方法建立桥梁,以便我们可以比较两个Any
。这样,对象就不必符合Equatable
协议。
protocol AnyObserverBox {
var hashValue: Int { get }
var base: Any { get }
func unbox<T: Hashable>() -> T
func isEqual(to other: AnyObserverBox) -> Bool
}
之后,我们制作符合 AnyObserverBox
的框。
struct HashableBox<Base: Hashable>: AnyObserverBox {
let _base: Base
init(_ base: Base) {
_base = base
}
var base: Any {
return _base
}
var hashValue: Int {
return _base.hashValue
}
func unbox<T: Hashable>() -> T {
return (self as AnyObserverBox as! HashableBox<T>)._base
}
func isEqual(to other: AnyObserverBox) -> Bool {
return _base == other.unbox()
}
}
此框包含我们稍后将创建的AnyObserver
的实际值。
最后我们制作 AnyObserver
。
struct AnyObserver {
private var box: AnyObserverBox
public var base: Any {
return box.base
}
public init<T>(_ base: T) where T: Observer {
box = HashableBox<T>(base)
}
}
extension AnyObserver: Hashable {
static func ==(lhs: AnyObserver, rhs: AnyObserver) -> Bool {
// Hey! We can do a comparison without Equatable protocol.
return lhs.box.isEqual(to: rhs.box)
}
var hashValue: Int {
return box.hashValue
}
}
完成所有这些后,我们可以:
var observers: [AnyObserver] = []
observers.append(AnyObserver(yourObject))
答案 1 :(得分:0)
实际上,你不能声明一个Set -or甚至是一个类型为Observable
的数组,这是因为在某个级别Observable
代表一个通用协议:
可观察 - &gt; Hashable - &gt;的 Equatable 强>:
包含:
public static func ==(lhs: Self ,rhs: Self ) - &gt;布尔
这就是为什么无法以 Heterogenous 方式使用它的原因。此外,您不能声明存在类型:
var object: Observable?
// error: protocol 'Observable' can only be used as a generic constraint
// because it has Self or associated type requirements
如果您想知道这种约束的原因是什么,我认为比较拖曳People
或拖曳Color
是合乎逻辑的,但不比较{{1}与People
。
那么,我们能做些什么呢?
作为一种解决方法,您可以将您的设置设为AnyHashable结构集(如评论中提到的@Leo):
AnyHashable类型转发相等比较和散列 对基础hashable值的操作,隐藏其特定的 基础类型。
如下:
Color
这是合法的。