这就是我想要的。我正在写一个非常简单的 event dispatcher (单击该链接以查看我的代码)。当我只有 listen()
时它工作正常和 fire()
方法。这是您可以使用它的方式:
struct UserHasBirthday: Event {
let name: String
init(name: String) {
self.name = name
}
}
let events = TestDispatcher()
events.listen {
(event: UserHasBirthday) in
print("Happy birthday \(event.name)!")
}
events.fire( UserHasBirthday(name: "John Doe") )
一切都很好,但现在我想添加一个功能,您可以将事件推送到队列中,然后一次性触发所有事件。这就是我添加 push 和 flush 方法的原因。
现在的问题是在flush()
方法我需要能够向下转换通用 Event
类型为给定的特定事件类型。否则 fire()
方法无效。
所以我想,也许我可以将类型信息保存在与事件本身相同的数组中。如您所见,我试图用一个元组来做到这一点。不幸的是,它不是那样工作的。
我认为,如果我能找到一种方法来生成变量 pushedEvents
接受像这样的通用类型:var pushedEvents = Array<E: Event>()
然后它可以工作。但我知道这样做的唯一方法是像这样将泛型分配给整个类:class TestDispatcher<E: Event> { }
,但是该类的每个实例只能用于一种特定类型的事件,我绝对不希望那样。
有没有人知道某种方法可以使这项工作正常进行?
最佳答案
This guy on reddit通过使用所谓的类型删除模式(我不知道该模式)为我提供了解决方案。
我编辑了他的代码以更多地满足我的需求,这就是我现在拥有的:
public protocol Event {}
public protocol ErasedListener {
func matches(eventType: Event.Type) -> Bool
func dispatchIfMatches(event: Event)
}
public struct Listener<T: Event>: ErasedListener {
let dispatch: T -> Void
public func matches(eventType: Event.Type) -> Bool {
return matches(String(eventType))
}
func matches(eventType: String) -> Bool {
return eventType == String(T.self)
}
public func dispatchIfMatches(event: Event) {
if matches(String(event.dynamicType)) {
dispatch(event as! T)
}
}
}
public protocol Dispatcher {
func listen<E: Event>(listener: E -> Void)
func fire(event: Event)
func queue<E: Event>(event: E)
func flushQueueOf<E: Event>(eventType: E.Type)
func flushQueue()
func forgetListenersFor<E: Event>(event: E.Type)
func emptyQueueOf<E: Event>(eventType: E.Type)
func emptyQueue()
}
public class MyDispatcher: Dispatcher {
var listeners = [ErasedListener]()
var queuedEvents = [Event]()
public init() {}
public func listen<E: Event>(listener: E -> Void) {
let concreteListener = Listener(dispatch: listener)
listeners.append(concreteListener as ErasedListener)
}
public func fire(event: Event) {
for listener in listeners {
listener.dispatchIfMatches(event)
}
}
public func queue<E: Event>(event: E) {
queuedEvents.append(event)
}
public func flushQueue() {
for event in queuedEvents {
fire(event)
}
emptyQueue()
}
public func emptyQueue() {
queuedEvents = []
}
public func flushQueueOf<E: Event>(eventType: E.Type) {
for event in queuedEvents where String(event.dynamicType) == String(eventType) {
fire(event)
}
emptyQueueOf(eventType)
}
public func forgetListenersFor<E: Event>(eventType: E.Type) {
listeners = listeners.filter { !$0.matches(eventType) }
}
public func emptyQueueOf<E: Event>(eventType: E.Type) {
queuedEvents = queuedEvents.filter { String($0.dynamicType) != String(eventType) }
}
}
示例用法
struct UserDied: Event {
var name: String
}
class UserWasBorn: Event {
let year: Int
init(year: Int) {
self.year = year
}
}
// you can use both classes and structs as events as you can see
let daveDied = UserDied(name: "Dave")
let bartWasBorn = UserWasBorn(year: 2000)
var events = MyDispatcher()
events.listen {
(event: UserDied) in
print(event.name)
}
events.listen {
(event: UserWasBorn) in
print(event.year)
}
events.queue(daveDied)
events.queue(UserWasBorn(year: 1990))
events.queue(UserWasBorn(year: 2013))
events.queue(UserDied(name: "Evert"))
// nothing is fired yet, do whatever you need to do first
events.flushQueue()
/*
This prints:
Dave
1990
2013
Evert
*/
// You could also have flushed just one type of event, like so:
events.flushQueueOf(UserDied)
// This would've printed Dave and Evert,
// but not the year-numbers of the other events
关于arrays - 如何使类属性数组成为泛型类型的异构数组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36241713/