事件驱动的 Swift

将代码的不同单元解耦对项目(们)的长期可维护性贡献巨大。 虽然观察者模式通过仅要求观察者遵循共同约定的接口(在 Swift 中为协议)来提供一定程度的解耦,但我们可以使用事件驱动模式更进一步。

使用事件驱动系统,各个代码单元之间绝对没有直接引用。 相反,每个独立的代码单元都会发出和监听事件(如果适用)。 事件只是一个包含不可变信息的结构化对象。 然后,每个代码单元可以根据收到的事件进行操作,并在该特定代码单元的上下文中执行任何必要的操作。

Event Driven Swift 是一个极其强大的库,专门用于在 Swift 语言中驱动您的事件驱动应用程序。

解耦拓扑

传统的软件设计原则要求通信对象直接相互引用,而事件驱动的设计模式消除了这种需求。

Topological Diagram showing Event-Driven ViewModel being updated via Events from the Data Model Repository

术语

理解此库及其支持示例/文档中使用的术语将极大地帮助您立即利用这些工具来生成极其强大、高性能、完全解耦且易于维护的事件驱动解决方案。

事件

事件 只是一个不可变的信息负载,可用于驱动逻辑和行为。

事件 视为类似于操作触发器。 为了响应收到已知类型的事件,一个不同的代码单元将根据在该事件有效负载中收到的信息执行适当的操作。

EventDrivenSwift 中,我们通常将事件 定义为符合 Eventable 协议的 struct

这是一个简单的例子

struct TemperatureEvent: Eventable {
    var temperatureInCelsius: Float
}

请注意,上面的示例 TemperatureEvent 是最基本的事件 示例,因为它仅包含一条信息。 实际上,您的事件 可以封装尽可能多的信息,只要它对于单个有凝聚力的操作触发器是合乎逻辑的。

重要提示: 事件 应该绝不包括任何引用类型的值(例如 class 实例)。 事件 需要是不可变的,这意味着在它们被分发后,它们的任何值都不可能更改。

事件队列

事件队列Eventable 对象的一个连续集合 (Array),只要队列 不为空,它将自动被处理。 队列 总是按照先进先出(或 FiFo)的顺序处理。

请注意,分发到队列事件 将始终在分发到堆栈事件 之后处理。

事件堆栈

事件堆栈 实际上与事件队列 相同,只是它以相反的顺序处理:后进先出(或 LiFo

请注意,分发到堆栈事件 将始终在分发到队列事件 之前处理。

事件优先级

事件 可以以以下优先级 之一分发到队列堆栈.highest 将首先处理 .high 将第二个处理 .normal 将第三个处理 .low 将第四个处理 .lowest 将最后处理

这意味着我们可以在分发点强制执行一定程度的执行顺序

分发

分发 是一个类似于广播 的术语。 当我们分发 一个事件 时,这意味着我们将该信息发送到每个正在监听该事件类型EventThread(请参阅下一节)。

一旦事件分发,就无法取消或修改。 这是设计使然。 可以把它想象成“你不能在你说了之后就不说它”。

事件 可以从代码中的任何位置分发,而不管哪个线程 正在调用它。 从这个意义上讲,事件 非常像一个发送后忘记 的过程。

EventThread

EventThread 是一个 class,它继承了此库提供的名为 EventThread 的基本类型。

在表面之下,EventThread 继承自 Thread,并且实际上被称为 Persistent Thread。 这意味着 Thread 通常会存在于您的特定应用程序需要它的时间那么长,甚至存在于您的应用程序的整个生命周期内。

与大多数线程不同,EventThread 专门构建为以尽可能低的系统资源占用运行。 当没有事件 等待您的 EventThread 处理时,该线程将完全不消耗 CPU 时间,并且实际上不消耗任何电量。

一旦您的 EventThread 收到它已订阅Eventable 类型的事件,它将自动唤醒 并处理其各自的队列堆栈 中任何等待的事件

注意: 任意数量的 EventThread 可以接收相同的事件。 这意味着您可以出于任何数量的目的,以任何数量的方式,以任何数量的结果来处理同一个事件

事件处理程序(或回调)

当您定义 EventThread 后代时,您将实现一个名为 registerEventListeners 的函数。 在此函数中(每次初始化 EventThread 后代类型的实例时都会自动调用该函数),您将注册您的 EventThread 感兴趣的 Eventable 类型; 并为每个类型定义一个合适的处理程序(或回调)方法,以便在这些事件 发生时处理它们。

您将在本文档后面的 用法 部分看到详细的示例,但这里要理解的关键是,对于您的 EventThread 有兴趣处理的每种 Eventable 类型,您将能够在一行代码中注册该事件 类型的事件处理程序

这使得管理和维护每个 EventThread 已被实现来处理的事件订阅 变得非常容易。

以性能为中心

EventDrivenSwift 专门设计用于在分发 事件 时以及处理 事件 时提供尽可能好的性能平衡。

考虑到这一点,EventDrivenSwift 默认提供了一个中央事件分发处理程序。 无论何时您通过队列堆栈 分发 一个事件,它都会立即在中央事件分发处理程序 中排队,然后它将通过其自己的线程 分发 到所有已注册的 EventThread

这意味着在指示事件 分发 和在调用线程的执行中继续之间几乎没有等待时间。

尽管以这种方式使用中介处理程序,但事件分发 和每个 EventThread 对该事件处理 之间的时间非常短! 这使得 EventDrivenSwift 对于性能关键型应用程序(包括视频游戏)非常有用!

构建于 Observable 之上

EventDrivenSwift 构建在我们的 Observable之上,并且 EventThread 继承自 ObservableThread,这意味着它支持完整的观察者模式 行为以及事件驱动 行为。

简而言之:您可以在代码中任何需要的地方观察 EventThread,包括从 SwiftUI 视图中。

这意味着您的应用程序可以动态更新您的视图以响应接收和处理的事件,使您的应用程序真正且完全多线程,而无需您生成代码来处理线程间通信。

构建于 ThreadSafeSwift 之上

EventDrivenSwift 也构建在我们的 ThreadSafeSwift之上,并且 EventDrivenSwift 中每种类型的每个公共方法和成员都专门设计为线程安全

强烈建议您使用 EventDrivenSwift 的自己的实现严格遵守最佳线程安全标准。 话虽如此,除非您定义一个专门用于在 UI 上显示信息的公开可访问的 varfunc,否则大多数使用纯事件驱动方法构建的后端实现都不需要过多地关注线程安全。

安装

Xcode 项目

选择 File -> Swift Packages -> Add Package Dependency 并输入 https://github.com/Flowduino/EventDrivenSwift.git

Swift Package Manager 项目

您可以在您自己的 Package 的 Package.swift 文件中使用 EventDrivenSwift 作为 Package 依赖项

let package = Package(
    //...
    dependencies: [
        .package(
            url: "https://github.com/Flowduino/EventDrivenSwift.git",
            .upToNextMajor(from: "5.0.0")
        ),
    ],
    //...
)

从那里,在任何需要它的 *您的* 包的目标中,将 EventDrivenSwift 称为“目标依赖项”。

targets: [
    .target(
        name: "YourLibrary",
        dependencies: [
          "EventDrivenSwift",
        ],
        //...
    ),
    //...
]

然后您可以在任何需要它的代码中执行 import EventDrivenSwift

用法

所以,既然我们已经了解了 EventDrivenSwift 是什么,它做什么,并且我们已经涵盖了许多重要的术语,让我们来看看我们实际上如何使用它。

定义一个事件 类型

您可以通过简单地从 Eventable 继承来将几乎任何 struct 类型转换为事件 类型

struct TemperatureEvent: Eventable {
    var temperatureInCelsius: Float
} 

它真的就是这么简单!

分发 一个事件

现在我们有了一个定义的事件 类型,让我们看看我们将如何分发 这种类型的事件

let temperatureEvent = TemperatureEvent(temperatureInCelsius: 23.25)

以上创建了我们的 TemperatureEvent 事件 类型的实例。 如果我们想通过具有 .normal 优先级队列 来分发它,我们可以像这样轻松地做到这一点

temperatureEvent.queue()

我们还可以自定义优先级

temperatureEvent.queue(priority: .highest)

以上将通过具有 .highest 优先级堆栈 分发事件。

通过堆栈 分发时,同样有效

temperatureEvent.stack()

以上将再次使用 .normal 优先级...

temperatureEvent.stack(priority: .highest)

以上将使用 .highest 优先级

计划分发 一个事件

4.2.0 版本将计划分发 引入到库中

temperatureEvent.scheduleQueue(at: DispatchTime.now() + TimeInterval().advanced(by: 4), priority: .highest)

以上将在 4 秒后通过具有最高优先级队列 分发 temperatureEvent

temperatureEvent.scheduleStack(at: DispatchTime.now() + TimeInterval().advanced(by: 4), priority: .highest)

以上将在 4 秒后通过具有最高优先级堆栈 分发 temperatureEvent

当您的用例需要在事件 的组合与其分发 以进行处理之间有固定或计算的延迟时,计划事件分发 是一大优势。

(接收和处理事件 - 方法 1) 定义一个 EventThread

所以,我们有一个事件 类型,并且我们能够通过队列堆栈 以我们想要的任何优先级分发 它。 现在我们需要一种方法来接收我们的 *TemperatureEvent以便我们可以用它们做一些事情。 一种方法是定义一个 EventThread来监听和处理我们的 TemperatureEvent`。

class TemperatureProcessor: EventThread {
    /// Register our Event Listeners for this EventThread
    override func registerEventListeners() {
        addEventCallback(onTemperatureEvent, forEventType: TemperatureEvent.self)
    }
    
    /// Define our Callback Function to process received TemperatureEvent Events
    func onTemperatureEvent(_ event: TemperatureEvent, _ priority: EventPriority, _ dispatchTime: DispatchTime) {

    }
}

在我们深入研究 onTemperatureEvent 的实现之前,它基本上可以对 TemperatureEvent 中提供的数据做任何我们想做的事情,让我们花点时间来了解上面的代码中发生了什么。

首先,TemperatureProcessor 继承自 EventThread,这是发生所有接收事件 和注册我们的监听器(或回调处理程序)的魔力的地方。

当创建 TemperatureProcessor 的实例时,将自动调用函数 registerEventListeners。 在此方法中,我们调用 addEventCallback 来注册 onTemperatureEvent,以便在每次分发 类型为 TemperatureEvent事件 时都会调用它。

我们的回调(或处理程序监听器事件)被称为 onTemperatureEvent,我们将在其中实现要对 TemperatureEvent 执行的任何操作

5.0.0 版本引入了新参数 dispatchTime,它将始终提供 EventDispatchedDispatchTime 引用。你可以使用它来确定 Delta(自 EventDispatched 以来经过了多少时间),这在你执行插值和/或外推时特别有用。

现在,让我们在 onTemperatureEvent 方法中实际对我们的 TemperatureEvent 做一些事情。

    /// An Enum to map a Temperature value onto a Rating
    enum TemperatureRating: String {
        case belowFreezing = "Below Freezing"
        case freezing = "Freezing"
        case reallyCold = "Really Cold"
        case cold = "Cold"
        case chilly = "Chilly"
        case warm = "Warm"
        case hot = "Hot"
        case reallyHot = "Really Hot"
        case boiling = "Boiling"
        case aboveBoiling = "Steam"
        
        static func fromTemperature(temperatureInCelsius: Float) -> TemperatureRating {
            if temperatureInCelsius < 0 { return .belowFreezing }
            else if temperatureInCelsius == 0 { return .freezing }
            else if temperatureInCelsius < 5 { return .reallyCold }
            else if temperatureInCelsius < 10 { return .cold }
            else if temperatureInCelsius < 16 { return .chilly }
            else if temperatureInCelsius < 22 { return .warm }
            else if temperatureInCelsius < 25 { return .hot }
            else if temperatureInCelsius < 100 { return .reallyHot }
            else if temperatureInCelsius == 100 { return .boiling }
            else { return .aboveBoiling }
        }
    }
    
    @ThreadSafeSemaphore public var temperatureInCelsius: Float = Float.zero
    @ThreadSafeSemaphore public var temperatureRating: TemperatureRating = .freezing
    
    func onTemperatureEvent(_ event: TemperatureEvent, _ priority: EventPriority, _ dispatchTime: DispatchTime) {
        temperatureInCelsius = event.temperatureInCelsius
        temperatureRating = TemperatureRating.fromTemperature(event.temperatureInCelsius)
    }
}

上面的代码旨在说明问题,而不是实用。 我们的 onTemperatureEventEvent 封装的 temperatureInCelsius 传递给一个公共变量(然后其他代码可以根据需要读取该变量),作为 EventThread 的一部分,并且还基于 Event 中接收到的温度值预先计算 TemperatureRating

最终,你的代码可以使用 EventPayload 数据做任何你想做的事情!

用于测试目前为止所有内容的 Playground 代码

目前你唯一缺少的是如何创建 EventListner 类型的实例。 实际上,这非常简单。 以下代码可以在 Playground 中运行

let temperatureProcessor = TemperatureProcessor()

这就是创建 TemperatureProcessor 实例所需的一切。

让我们添加一行来打印 temperatureProcessor 的初始值

print("Temp in C: \(temperatureProcessor.temperatureInCelsius)")
print("Temp Rating: \(temperatureProcessor.temperatureRating)")

我们现在可以 Dispatch 一个 TemperatureEvent 以便由 temperatureProcessor 处理

TemperatureEvent(temperatureInCelsius: 25.5).queue()

因为 Events异步处理的,并且因为这只是一个 Playground 测试,所以让我们添加一个 1 秒的睡眠时间,以使 TemperatureProcessor 有时间接收和处理 Event注意: 实际上,这只需要不到 1 毫秒即可处理!

sleep(1)

现在让我们再次打印相同的值,看看它们是否已更改

print("Temp in C: \(temperatureProcessor.temperatureInCelsius)")
print("Temp Rating: \(temperatureProcessor.temperatureRating)")

现在你有一小段 Playground 代码可以直观地确认你的 Events 正在被处理。 你可以修改它以查看发生了什么。

观察 EventThread

请记住,EventThread 也是可观察的,因此我们不仅可以接收和操作 Events,还可以响应 Events 通知 Observers

让我们看一个基于上述示例的简单示例。 我们将从定义一个 Observer Protocol 开始

protocol TemperatureProcessorObserver: AnyObject {
    func onTemperatureEvent(temperatureInCelsius: Float)
}

现在让我们修改我们在上一个示例中实现的 onTemperatureEvent 方法

    func onTemperatureEvent(_ event: TemperatureEvent, _ priority: EventPriority, _ dispatchTime: DispatchTime) {
        temperatureInCelsius = event.temperatureInCelsius
        temperatureRating = TemperatureRating.fromTemperature(event.temperatureInCelsius)
        
        /// Notify any Observers...
        withObservers { (observer: TemperatureProcessorObserver) in
            observer.onTemperatureEvent(temperatureInCelsius: event.temperatureInCelsius)
        }
    }

现在,每次 EventThread 处理 TemperatureEvent 时,它还会通知任何直接的 Observers

应该注意的是,此功能是对事件驱动行为的补充,因为没有“一刀切”的解决方案可以满足软件中的所有需求。 通常需要结合使用各种方法才能获得最佳结果。

互惠事件

通常,系统不仅消费信息,还返回信息(结果)。 这不仅在事件驱动系统中如此,而且也很容易实现。

让我们再次扩展前面的示例,这次发出一个互惠的 Event 来封装温度,以及我们响应 TemperatureEvent 计算出的 TemperatureRating

我们将首先定义互惠的 Event 类型

enum TemperatureRatingEvent: Eventable {
    var temperatureInCelsius: Float
    var temperatureRating: TemperatureRating
}

定义了 Event 类型后,我们现在可以再次扩展我们的 onTemperatureEventDispatch 我们的互惠 TemperatureRatingEvent

    func onTemperatureEvent(_ event: TemperatureEvent, _ priority: EventPriority, _ dispatchTime: DispatchTime) {
        temperatureInCelsius = event.temperatureInCelsius
        temperatureRating = TemperatureRating.fromTemperature(event.temperatureInCelsius)
        
        /// Notify any Observers...
        withObservers { (observer: TemperatureProcessorObserver) in
            observer.onTemperatureEvent(temperatureInCelsius: event.temperatureInCelsius)
        }
        
        /// Dispatch our Reciprocal Event...
        TemperatureRatingEvent(
            temperatureInCelsius = temperatureInCelsius,
            temperatureRating = temperatureRating
        ).queue()
    }

如你所见,我们可以在单个操作中创建和 Dispatch 一个 Event。 这是因为 Events 应被视为“发射后不管”。 如果你希望稍后在同一操作中使用它的值,则只需在 Dispatching Method 中保留 Event 的副本即可。 否则,只需创建它并一起 Dispatch 它,如上所示。

现在我们已经完成了这些基本的使用示例,看看你是否可以生成自己的 EventThread 来处理 TemperatureRatingEvent。 你需要实现此目的的所有内容已在本文档中演示。

UIEventThread

2.0.0 版本引入了 UIEventThread 基类,其操作方式与 EventThread 完全相同,但值得注意的是,你注册的 Event 回调将始终MainActor(或“UI 线程”)上调用。 只要必须在一个或多个 Event 回调上在 MainActor 上执行,你就可以简单地从 UIEventThread 而不是 EventThread 继承。

(接收和处理事件 - 方法 2)EventListener

3.0.0 版本将 EventListener 概念引入了库。 这些是一种通用的方法(可以在你定义的任何 class 中使用),用于接收从代码中的任何位置 DispatchEvents,并且使用起来需要少得多的代码

EventListener 是一种通用方法,可以在代码中的任何位置订阅 Events,而无需定义并在 EventThread 的约束下运行。

按照设计,EventDrivenSwift 提供了一个中央事件侦听器,如果你的任何代码通过引用 Eventable 类型来为 Event 注册一个 Listener,该侦听器将自动初始化。

重要提示: EventListener(默认情况下)将从 Listener 注册的同一线程(或 DispatchQueue)调用关联的 Callbacks! 这是一个非常有用的行为,因为它意味着从 MainActor(或“UI 线程”)注册的 Listeners 将始终在该线程上执行,而无需你添加额外的开销或代码。

让我们在一些任意的 class 中注册一个简单的 Listener。 对于此示例,让我们生成一个假设的 View Model,该模型将监听 TemperatureRatingEvent,并使拥有的 View 无效以显示新收到的值。

为了这个例子,让我们以纯 SwiftUI 的方式定义它,而不利用我们的 Observable

class TemperatureRatingViewModel: ObservableObject {
    @Published var temperatureInCelsius: Float
    @Published var temperatureRating: TemperatureRating
    
    var listenerHandle: EventListenerHandling?
    
    internal func onTemperatureRatingEvent(_ event: TemperatureRatingEvent, _ priority: EventPriority, _ dispatchTime: DispatchTime) {
        temperatureInCelsius = event.temperatureInCelsius
        temperatureRating = event.temperatureRating
    }
    
    init() {
        // Let's register our Event Listener Callback!
        listenerHandle = TemperatureRatingEvent.addListener(self, onTemperatureRatingEvent)
    }
}

它确实是如此简单!

我们可以直接引用 Eventable 类型,并调用 addListener 方法(自动绑定到所有 Eventable 类型)来注册我们的 Listener

在上面的示例中,只要 Reciprocal Event 名为 TemperatureRatingEvent 被 dispatched,任何 TemperatureRatingViewModel 实例的 onTemperatureRatingEvent 方法都会被调用,在该 Event 的上下文中!

不要担心管理你的 Listener 的生命周期! 如果拥有 Listener 的对象被销毁,Listener 将自动为你注销!

如果你需要你的 Event CallbackListener 的线程上执行,从 3.1.0 版本开始... 你可以!

listenerHandle = TemperatureRatingEvent.addListener(self, onTemperatureRatingEvent, executeOn: .listenerThread)

记住: 当在 .listenerThread 上执行 Event Callback 时,你需要确保你的 Callback 及其使用的所有资源都是 100% 线程安全的! 重要:.listnerThread 上执行 Event Callback 可能会延迟其他 Event Callbacks 的调用。 仅在必要时使用此选项。

你也可以在 ad-hoc Task 上执行你的 Event Callback

listenerHandle = TemperatureRatingEvent.addListener(self, onTemperatureRatingEvent, executeOn: .taskThread)

记住: 当在 .taskThread 上执行 Event Callback 时,你需要确保你的 Callback 及其使用的所有资源都是 100% 线程安全的!

关于上面示例的另一点是 listenerHandle。 每当你注册一个 Listener 时,它将返回一个 EventListenerHandling 对象。 你可以使用此值随时取消注册你的 Listener

    listenerHandle.remove()

这将删除你的 Listener Callback,这意味着当 TemperatureRatingEventDispatched 时,它将不再被调用。

注意: 这是 4.1.0 版本的改进,而不是使用以前版本中未类型化的 UUID

EventListenerEventDrivenSwift 的一个极其通用且非常强大的补充。

具有仅最新兴趣的 EventListener

该库的 4.3.0 版本引入了仅最新侦听器的概念。 仅最新侦听器是一个 Listener,它仅针对其请求的 Event Type 的最新 Event 被调用。 如果在此类型的队列/堆栈中存在许多较旧的 Events 等待处理,它们将被简单地跳过... 并且只有最新的 Event 才会调用你的 Listener

我们使你能够非常简单地将你的 Listener 配置为 仅最新侦听器。 采用前面的代码示例,我们可以简单地对其进行如下修改

class TemperatureRatingViewModel: ObservableObject {
    @Published var temperatureInCelsius: Float
    @Published var temperatureRating: TemperatureRating
    
    var listenerHandle: EventListenerHandling?
    
    internal func onTemperatureRatingEvent(_ event: TemperatureRatingEvent, _ priority: EventPriority, _ dispatchTime: DispatchTime) {
        temperatureInCelsius = event.temperatureInCelsius
        temperatureRating = event.temperatureRating
    }
    
    init() {
        // Let's register our Event Listener Callback!
        listenerHandle = TemperatureRatingEvent.addListener(self, onTemperatureRatingEvent, interestedIn: .latestOnly)
    }
}

通过在针对任何 Eventable 类型调用 addListener 时包含 interestedIn 可选参数,并为此参数传递 .latestOnly 值,我们定义此 Listener 仅对要 Dispatched最新 TemperatureRatingEvent 感兴趣。 如果在队列/堆栈中累积了许多 TemperatureRatingEvents,则上述定义的 Listener 将简单地丢弃任何较旧的 Events,而仅针对最新的 Event 调用。

具有最大年龄兴趣的 EventListener

该库的 5.1.0 版本引入了最大年龄侦听器的概念。 最大年龄侦听器是一个 Listener,它仅针对比定义的最大年龄更年轻的其注册的 Event TypeEvents 被调用。 任何比定义的最大年龄大的 Event 都将被跳过,而任何年轻Event 都将调用你的 Listener

我们使你能够简单地配置你的 Listener 以定义最大年龄兴趣。 采用前面的代码示例,我们可以简单地对其进行如下修改

class TemperatureRatingViewModel: ObservableObject {
    @Published var temperatureInCelsius: Float
    @Published var temperatureRating: TemperatureRating
    
    var listenerHandle: EventListenerHandling?
    
    internal func onTemperatureRatingEvent(_ event: TemperatureRatingEvent, _ priority: EventPriority, _ dispatchTime: DispatchTime) {
        temperatureInCelsius = event.temperatureInCelsius
        temperatureRating = event.temperatureRating
    }
    
    init() {
        // Let's register our Event Listener Callback!
        listenerHandle = TemperatureRatingEvent.addListener(self, onTemperatureRatingEvent, interestedIn: .youngerThan, maximumAge: 1 * 1_000_000_000)
    }
}

在上面的代码示例中,maximumAge 是以纳秒为单位定义的值。 考虑到这一点,1 * 1_000_000_000 将是 1 秒。 这意味着,任何比 1 秒更旧的 TemperatureRatingEvent 都将被 Listener 忽略,而任何比 1 秒更年轻TemperatureRatingEvent 都将调用 onTemperatureRatingEvent 方法。

Event 用法的上下文具有已知的、固定的到期时间时,此功能非常有用。

具有自定义事件过滤兴趣的 EventListener

该库的 5.2.0 版本引入了 Listeners自定义事件过滤概念。

现在,在为 Eventable 类型注册 Listener 时,你可以指定一个 customFilter Callback,该 Callback 最终返回一个 Bool,其中 true 表示 ListenerEvent 感兴趣,而 false 表示 ListenerEvent 感兴趣。

我们使你能够简单地为你的 Listener 配置一个自定义过滤器。 采用前面的代码示例,我们可以简单地对其进行如下修改

class TemperatureRatingViewModel: ObservableObject {
    @Published var temperatureInCelsius: Float
    @Published var temperatureRating: TemperatureRating
    
    var listenerHandle: EventListenerHandling?
    
    internal func onTemperatureRatingEvent(_ event: TemperatureRatingEvent, _ priority: EventPriority, _ dispatchTime: DispatchTime) {
        temperatureInCelsius = event.temperatureInCelsius
        temperatureRating = event.temperatureRating
    }
    
    internal func onTemperatureRatingEventFilter(_ event: TemperatureRatingEvent, _ priority: EventPriority, _ dispatchTime: DispatchTime) -> Bool {
        if event.temperatureInCelsius > 50 { return false } // If the Temperature is above 50 Degrees, this Listener is not interested in it!
        return true // If the Temperature is NOT above 50 Degrees, the Listener IS interested in it!
    }
    
    init() {
        // Let's register our Event Listener Callback!
        listenerHandle = TemperatureRatingEvent.addListener(self, onTemperatureRatingEvent, interestedIn: .custom, customFilter: onTemperatureRatingEventFilter)
    }
}

上面的代码将确保仅针对 temperatureInCelsius 小于或等于 50 摄氏度的 TemperatureRatingEvent 调用 onTemperatureRatingEvent 方法。 任何 temperatureInCelsius 大于 50 的 TemperatureRatingEvent 都将被此 Listener 忽略。

EventPool

4.0.0 版本引入了极其强大的 EventPool 解决方案,使得可以创建 EventThread 的托管组,其中入站 Events 将在任何给定时刻被定向到 EventPool 中最好的 EventThread

EventDrivenSwift 使得为任何给定的 EventThread 类型生成 EventPool 变得非常简单。

要从之前的 TemperatureProcessor 示例创建一个 EventPool,我们可以使用一行代码

var temperatureProcessorPool = EventPool<TemperatureProcessor>(capacity: 5)

上面的示例将创建一个 TemperatureProcessorEventPool,其初始容量5 个实例。 这意味着你的程序可以并发地处理 5TemperatureEvents。 显然,对于像我们之前的示例一样简单且快速完成的过程,不需要生成 EventPool,但你可以将此示例用于你自己的、更复杂且耗时的 EventThread 实现,以立即并行化它们。

EventPool 允许您在初始化时指定最适合上下文的 Balancer (平衡器)。

var temperatureProcessorPool = EventPool<TemperatureProcessor>(capacity: 5, balancer: EventPoolRoundRobinBalancer())

上面的例子将使用 EventPoolRoundRobinBalancer 实现,它只是将每个入站的 Eventable 指向池中的下一个 EventThread,在使用池中的最后一个 EventThread 后,会循环回到第一个 EventThread

在 4.0.0 版本中还有另一个 Balancer (平衡器)可用

var temperatureProcessorPool = EventPool<TemperatureProcessor>(capacity: 5, balancer: EventPoolLowestLoadBalancer())

上面的例子将使用 EventPoolLowestLoadBalancer 实现,它只是将每个入站的 Eventable 指向池中 Queue (队列)和 Stack (堆栈)中待处理的 Eventable 数量最少的 EventThread

注意: 如果没有声明 balancerEventPool 默认会使用 EventPoolRoundRobinBalancer

即将推出的功能

EventDrivenSwift 是一个不断发展和改进的库,以下是您可以在未来版本中期待的功能列表。

5.1.0 版本 (如果需要破坏接口的更改,则为 6.0.0 版本)

许可证

EventDrivenSwift 在 MIT 许可证下可用。 有关更多信息,请参见 LICENSE 文件

加入我们的 Discord

如果您需要其他支持,或者想讨论 EventDrivenSwift,Swift 或与 Flowduino 相关的任何其他主题,可以 加入我们的 Discord