自 WWDC 以来,诸如 SketchyTech 、 David Owens 和 Ray Wenderlich 之类的博主已经写了很多关于 Swift 中面向协议编程的文章,我认为是时候发表自己的看法了。
在 ActionScript 中使用事件调度 多年后,协议扩展似乎是在 Swift 中实现类似模式的完美技术。实际上,协议扩展提供了直接的优势,即我可以将事件分派添加到任何类型的对象,而无需该对象扩展基类。例如,不仅用户界面组件可以调度事件,值对象和数据结构也可以: 非常适合 MVVM 模式 ,其中视图可以对视图模型上的事件做出反应以更新自身。
我的项目 Protocol Extension Event Dispatcher 包含一个演示应用程序,其中包含一些用户界面组件:滑块、步进器、标签和按钮。有一个“模型”:一个整数,当它的值通过这些组件发生变化时,它会调度一个变化事件。最终结果是,当用户与任何组件交互时,整个用户界面都会通过事件进行更新,以反映变化。
这并不是要完整地实现 Swift 中的事件调度,而是演示 Swift 中面向协议编程的可能性。如需更完整的版本,请查看 ActionSwift 。
让我们来看看我的代码是如何工作的。首先,我有我的协议 EventDispatcher,它定义了一些方法。这是一个类协议,因为我们希望调度程序是一个单一的引用对象:
protocol EventDispatcher: class
{
func addEventListener(type: EventType, handler: EventHandler)
func removeEventListener(type: EventType, handler: EventHandler)
func dispatchEvent(event: Event)
}
符合 EventDispatcher 的对象的每个实例都需要一个小的事件侦听器存储库,我将其存储为一个字典,其中事件类型作为键,一组事件处理程序作为值。
第一个绊脚石是扩展可能不包含存储的属性。有几个选项可以解决这个问题:我可以创建一个全局存储库,或者我可以使用 objc_getAssociatedObject 和 objc_setAssociatedObject。这些功能都是我使用一些简单的语法将事件侦听器附加到每个 EventDispatcher 实例。我的 addEventListener 默认实现的代码如下所示:
protocol EventDispatcher: class
{
func addEventListener(type: EventType, handler: EventHandler)
func removeEventListener(type: EventType, handler: EventHandler)
func dispatchEvent(event: Event)
}
对于给定的类型和事件处理程序,我检查是否存在现有的 EventListeners 对象,如果存在,我检查该对象是否具有该类型的条目并相应地创建或更新值。一旦我有了最新的 EventListeners 对象,我就可以用 objc_setAssociatedObject 将它写回。
以类似的方式,对于 dispatchEvent(),我查询关联对象,检查事件类型的处理程序,如果有则执行它们:
protocol EventDispatcher: class
{
func addEventListener(type: EventType, handler: EventHandler)
func removeEventListener(type: EventType, handler: EventHandler)
func dispatchEvent(event: Event)
}
我创建了一个简单的包装器,它利用泛型允许任何数据类型在发生变化时分派事件:
protocol EventDispatcher: class
{
func addEventListener(type: EventType, handler: EventHandler)
func removeEventListener(type: EventType, handler: EventHandler)
func dispatchEvent(event: Event)
}
我的演示应用程序使用 DispatchingValue 来包装一个整数:
protocol EventDispatcher: class
{
func addEventListener(type: EventType, handler: EventHandler)
func removeEventListener(type: EventType, handler: EventHandler)
func dispatchEvent(event: Event)
}
...通过添加事件侦听器在更改时更新用户界面控件:
protocol EventDispatcher: class
{
func addEventListener(type: EventType, handler: EventHandler)
func removeEventListener(type: EventType, handler: EventHandler)
func dispatchEvent(event: Event)
}
我还在 UIControl 上创建了一个扩展,使所有 UI 控件都符合 EventDispatcher 并调度更改和点击事件:
protocol EventDispatcher: class
{
func addEventListener(type: EventType, handler: EventHandler)
func removeEventListener(type: EventType, handler: EventHandler)
func dispatchEvent(event: Event)
}
因此,例如,我的滑块可以在用户更改其值时更新 dispatchingValue:
protocol EventDispatcher: class
{
func addEventListener(type: EventType, handler: EventHandler)
func removeEventListener(type: EventType, handler: EventHandler)
func dispatchEvent(event: Event)
}
...这又将调用 dispatchingValueChangeHandler 并更新其他用户界面组件。我的重置按钮在点击时将 dispatchingValue 的值设置为零:
protocol EventDispatcher: class
{
func addEventListener(type: EventType, handler: EventHandler)
func removeEventListener(type: EventType, handler: EventHandler)
func dispatchEvent(event: Event)
}
我希望这篇文章能让您领略到面向协议编程所提供的不可思议的力量。再一次,我的项目可以在 我的 GitHub 存储库 中找到。享受!