考虑这个例子:
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
Run Code Online (Sandbox Code Playgroud)
People 和 Color 都符合Observable协议,协议也继承自Hashable协议。我想将这些存储在observers集合中。
using 'Observable' as a concrete type conforming to protocol
'Hashable' is not supported
Run Code Online (Sandbox Code Playgroud)
是否可以在 Swift 中进行异构 Set?
There is a way to make it possible. (Inspired by Apple's implementation)
Before we begin, this is what we want to build.
protocol Observer: Hashable {
associatedtype Sender: Observable
func valueDidChangeInSender(_ sender: Sender, keypath: String, newValue: Any)
}
Run Code Online (Sandbox Code Playgroud)
The source of this problem is the use of Self that force the array to be Homogenous. You can see it here:
The most important change is that it stop the protocol from being usable as a type.
That makes us can't do:
var observers: [Observer] = [] // Observer is not usable as a type.
Run Code Online (Sandbox Code Playgroud)
Therefore, we need another way to make it work.
We don't do
var observers: [AnyHashable] = []
Run Code Online (Sandbox Code Playgroud)
Because AnyHashable will not constrain the object to conform Observer protocol. Instead, we can wrap the Observer object in the AnyObserver wrapper like this:
var observers: [AnyObserver] = []
observers.append(AnyObserver(yourObject))
Run Code Online (Sandbox Code Playgroud)
This will make sure the value of AnyObserver struct conforms to Observer protocol.
根据 WWDC 2015: Protocol-Oriented Programming in 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
}
Run Code Online (Sandbox Code Playgroud)
之后,我们制作符合 的盒子 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()
}
}
Run Code Online (Sandbox Code Playgroud)
此框包含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
}
}
Run Code Online (Sandbox Code Playgroud)
有了所有这些,我们可以做到:
var observers: [AnyObserver] = []
observers.append(AnyObserver(yourObject))
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
642 次 |
| 最近记录: |