Flow 是一个轻量级的 Swift 库,用于面向操作编程。它允许您轻松定义自己的原子操作,并包含一个丰富的可重用操作库,这些操作可以分组、排序、排队和重复。
使用 Flow 主要是将您的代码分解成多个原子部分,这些部分被称为 操作。每个操作定义了一个工作体,可以在应用程序或库中轻松重用。
操作可以执行任何同步或异步的操作,其范围完全由您决定。然而,操作驱动编程的真正力量在于您创建操作组、序列和队列。操作可以使代码变得更加简单,无论是异步操作还是需要在多个位置执行工作的情况。
通过在自定义对象中遵循 FlowOperation
创建自己的操作。所需做的只是实现一个带有完成处理程序的方法。它可以按照您希望的方式初始化,可以是 class
或 struct
。
使用任何内置操作,例如 FlowClosureOperation
、FlowDelayOperation
等。
使用 FlowOperationSequence
创建操作序列(按顺序执行),使用 FlowOperationGroup
创建组(同时执行),或使用 FlowOperationQueue
创建队列(可以连续添加操作)。
假设我们正在开发一款游戏,并想要执行一系列动画,其中包括一个 Player
攻击一个 Enemy
,摧毁它,然后播放胜利动画。当然,这可以通过使用完成处理程序闭包来完成
player.moveTo(enemy.position) {
player.performAttack() {
enemy.destroy() {
player.playVictoryAnimation()
}
}
}
然而,这很快变得难以推理并调试,特别是当我们开始添加多个需要同步的动画时。假设我们决定在我们的游戏中实现一个新的 旋风攻击,它可以摧毁多个敌人,并且我们想在播放胜利动画之前摧毁所有敌人。我们将不得不这样做
player.moveTo(mainEnemy.position) {
player.performAttack() {
var enemiesDestroyed = 0
for enemy in enemies {
enemy.destroy({
enemiesDestroyed += 1
if enemiesDestroyed == enemies.count {
player.playVictoryAnimation()
}
})
}
}
}
显然,我们添加到动画中的内容越多,它就越容易出错,调试也越困难。如果我们能让动画(或任何其他任务序列)随着复杂性的增加而优雅地扩展,那岂不是很好?
让我们用流程来实现上述内容。我们首先定义在动画过程中需要执行的所有任务为 操作
/// Operation that moves a Player to a destination
class PlayerMoveOperation: FlowOperation {
private let player: Player
private let destination: CGPoint
init(player: Player, destination: CGPoint) {
self.player = player
self.destination = destination
}
func perform(completionHandler: @escaping () -> Void) {
self.player.moveTo(self.destination, completionHandler: completionHandler)
}
}
/// Operation that performs a Player attack
class PlayerAttackOperation: FlowOperation {
private let player: Player
init(player: Player) {
self.player = player
}
func perform(completionHandler: @escaping () -> Void) {
self.player.performAttack(completionHandler)
}
}
/// Operation that destroys an enemy
class EnemyDestroyOperation: FlowOperation {
private let enemy: Enemy
init(enemy: Enemy) {
self.enemy = enemy
}
func perform(completionHandler: @escaping () -> Void) {
self.enemy.destroy(completionHandler)
}
}
/// Operation that plays a Player victory animation
class PlayerVictoryOperation: FlowOperation {
private let player: Player
init(player: Player) {
self.player = player
}
func perform(completionHandler: @escaping () -> Void) {
self.player.playVictoryAnimation()
completionHandler()
}
}
其次,我们将使用上述操作来实现我们的动画
let moveOperation = PlayerMoveOperation(player: player, destination: mainEnemy.position)
let attackOperation = PlayerAttackOperation(player: player)
let destroyEnemiesOperation = FlowOperationGroup(operations: enemies.map({
return EnemyDestroyOperation(enemy: $0)
}))
let victoryOperation = PlayerVictoryOperation(player: player)
let operationSequence = FlowOperationSequence(operations: [
moveOperation,
attackOperation,
destroyEnemiesOperation,
victoryOperation
])
operationSequence.perform()
虽然使用操作需要写更多的代码;但这种方法有一些显著的优点。
首先;现在我们可以使用 FlowOperationGroup
来确保在继续之前所有敌人的动画都已完成,通过这种方式,我们已经减少了动画内部需要保持的状态。
其次;动画的各个部分现在都是独立的操作,不需要彼此知晓,这使得它们更容易测试和调试 - 并且也可以在我们游戏的其余部分中重用。
FlowOperation
用来声明自定义操作。
FlowOperationCollection
用来声明自定义操作集合。
FlowClosureOperation
运行闭包的操作,执行后直接返回。
FlowAsyncClosureOperation
运行闭包的操作,然后在闭包调用完成处理程序之前等待,才会结束。
FlowDelayOperation
在完成前等待一定延迟的操作。在序列和队列中很有用。
FlowOperationGroup
用于将一系列操作组合在一起,当该组执行时,同时执行这些操作。
FlowOperationSequence
用于编排一系列操作,顺序执行。
FlowOperationQueue
队列,空闲时立即执行下一个操作。新操作可以持续添加。
FlowOperationRepeater
用于重复操作,可以选择在重复之间使用间隔。
NSOperations
是非常棒的,肯定是 Flow 的主要灵感之一。然而,NSOperations
比较重,可能需要很长时间来实现。Flow 设计来具有 NSOperations
的功能,但更易于使用。它也是100%使用 Swift 编写的,这使得它非常适合以 Swift 为基础的项⽬。
Flow 支持 Apple 的所有当前平台,以下为最低版本要求:
Flow 的当前版本支持 Swift 3。如果您需要 Swift 2 支持,可以改用 版本 1.1,或使用 swift 2
分支。
CocoaPods
在您的 Podfile
中添加以下行 pod "FlowOperations"
Carthage
在您的 Cartfile
中添加以下行 github "johnsundell/flow"
手动
克隆存储库,并将文件 Flow.swift
拖入您的 Xcode 项目中。
Swift 包管理器
在您的 Package.swift
中添加以下行 .Package(url: "https://github.com/johnsundell/flow.git", majorVersion: 2)
如需支持、反馈以及有关Flow的新闻,请关注我的Twitter: @johnsundell。