这就是我想要的。我写的是一个非常简单的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") )
这一切都很好,但现在我想添加一个功能,你可以将事件推送到队列,然后立即将它们全部解雇。这就是我添加推送和刷新方法的原因。
现在的问题是,在flush()
方法中,我需要能够将通用Event
类型向下转换为给定的特定事件类型。否则fire()
方法无效。
所以我想,也许我可以将类型信息保存在与事件本身相同的数组中。正如你所看到的,我试图用元组做到这一点。不幸的是,它不能那样工作。
我认为,如果我能找到一种方法让变量pushedEvents
接受这样的通用类型:var pushedEvents = Array<E: Event>()
那么它可以工作。但我知道这样做的唯一方法是将这个泛型分配给整个类,如:class TestDispatcher<E: Event> { }
,但那个类的每个实例只能用于一个特定类型的事件,我绝对不会想要那个。
有人知道某种方法可以使这项工作吗?
答案 0 :(得分:1)
问题是Swift不允许将类型转换为元类型。
一种解决方法是在{{1}的Event
函数的开关案例中包含符合Dispatcher
的所有类型(至少是您在flush()
中使用的那些类型) } .class。它不像我认为你正在寻找的功能那样通用,而as you've shown with your own answer,类型擦除就是通往这里的方式。但是,我将保留原始答案,因为它解释了为什么您尝试转换为元类型的原始方法不起作用。
TestDispatcher
//
public protocol Event {}
public enum Listener<E: Event> {
public typealias T = E -> ()
}
public protocol Dispatcher {
func listen<E: Event>(listener: Listener<E>.T)
func fire<E: Event>(event: E)
func push<E: Event>(event: E)
func flush()
}
使用示例:
public class TestDispatcher: Dispatcher {
var listeners = [String:[Any]]()
var pushedEvents = [Event]()
public init() {}
public func listen<E: Event>(listener: Listener<E>.T) {
var listeners = self.listeners[String(E.self)] ?? []
listeners += [listener] as [Any]
self.listeners[String(E.self)] = listeners
}
public func fire<E: Event>(event: E) {
listeners[String(E.self)]?.forEach {
let f = $0 as! Listener<E>.T
f(event)
}
}
public func push<E: Event>(event: E) {
pushedEvents = pushedEvents + [event]
}
/* Include a switch case over all types conforming to Event ... */
public func flush() {
for event in pushedEvents {
switch event {
case let ev as UserHasBirthday: fire(ev)
case let ev as UserWonTheLottery: fire(ev)
case _: print("Unknown event type.")
}
}
}
}
答案 1 :(得分:1)
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