PeakOperation是一个Swift微框架,为Operation
提供了增强和便利性。它是Peak框架的一部分。
并发操作
ConcurrentOperation
是一个抽象的 Operation
子类,可以异步执行工作。您通过覆盖 execute()
来执行工作,并在完成时调用 finish()
来完成操作。
class MyOperation: ConcurrentOperation {
override func execute() {
print("Hello World!")
finish()
}
}
let queue = OperationQueue()
let operation = MyOperation()
operation.enqueue(on: queue)
这意味着您可以在 execute()
内执行异步工作,例如执行一个 URLSession
请求。
操作链
Operation
提供了在操作之间添加依赖的能力。PeakOperation建立在这个功能之上,并使其通过一个易于使用的API来实现。
let firstOperation = ...
let secondOperation = ...
firstOperation
.then(do: secondOperation)
.enqueue()
在上面的例子中,secondOperation
将在 firstOperation
完成后运行。
您还可以在操作链上调用 enqueueWithProgress()
或在单个操作上调用 overallProgress()
来跟踪它们的进度。
let progress: Progress = firstOperation
.then(do: secondOperation)
.enqueueWithProgress()
// or
let progress: Progress = secondOperation.overallProgress()
传递结果
在操作之间添加依赖很有用,但操作之间传递结果才是PeakOperation真正闪耀的地方。PeakOperation包括两个您的操作可以遵从的协议:ProducesResult
和 ConsumesResult
。
假设我们有三个操作。第一个产生一个 Result<Int, Error>
。
class IntOperation: ConcurrentOperation, ProducesResult {
var output: Result<Int, Error>
override func execute() {
output = Result { 1 }
finish()
}
}
第二个操作消费一个 Result<Int, Error>
并产生一个 Result<String, Error>
。它解包其输入,加1,将其转换为字符串,然后设置其输出。
class AddOneOperation: ConcurrentOperation, ConsumesResult, ProducesResult {
var input: Result<Int, Error>
var output: Result<String, Error>
override func execute() {
output = Result { "\(try input.get() + 1)" }
finish()
}
}
最后一个操作消费一个 Result<String, Error>
。它解包它并将输出打印到控制台。
class PrintOperation: ConcurrentOperation, ConsumesResult {
var input: Result<String, Error>
override func execute() {
do {
print("Hello \(try input.get())!")
} catch { }
finish()
}
}
使用 passesResult(to:)
,这三个操作可以被串联在一起!
IntOperation()
.passesResult(to: AddOneOperation())
.passesResult(to: PrintOperation())
.enqueue()
// Hello 2!
只要输入类型与输出类型匹配,就可以在遵循协议的任何操作之间传递结果。
如果有任何操作失败并且其结果为.failure
,则结果仍将传递到下一个操作。处理错误的责任在于您,可能通过重新抛出错误适当处理。
class RethrowingOperation: ConcurrentOperation, ConsumesResult, ProducesResult {
var input: Result<String, Error>
var output: Result<String, Error>
override func execute() {
do {
let _ = try input.get()
output = ...
} catch {
output = Result { throw error }
}
finish()
}
}
这样,任何操作都可以失败,并且您仍然可以在最后检索到错误。
let failingOperation = ...
let successfulOperation = ...
let anotherSuccessfulOperation = ...
failingOperation
.passesResult(to: successfulOperation)
.passesResult(to: anotherSuccessfulOperation)
.enqueue()
anotherSuccessfulOperation.addResultBlock { result in
// result would contain the error from failingOperation
// even though the other operations still ran
}
分组
GroupChainOperation
接受一个操作和它的依赖操作,并在内部队列上执行它们。操作的最终结果是保留的,并且按照这种方式检查,以使得该操作能够产生类型为Result
的结果 - 数据值丢失,但.success
/.failure
会被保留。这允许您将不同输入/输出不兼容的组连接在一起。
// would otherwise produce String, now produces Void
let group1 = intOperation
.passesResult(to: stringOperation)
.group()
// Would otherwise accept Bool, now consumes Void
let group2 = boolOperation
.passesResult(to: anyOperation)
.group()
group1
.passesResult(to: group2)
.enqueue()
重试
有时一个操作可能会失败。也许您正在处理一个不可靠的Web服务或连接。为此,您可以从RetryingOperation
派生子类。这是一种ProducesResult
的操作,如果结果为.failure
,它将使用给定的retryStrategy
闭包再次尝试。
class MyRetryingOperation: RetryingOperation<AnyObject> {
override func execute() {
output = Result { throw error }
finish()
}
}
let operation = MyRetryingOperation()
operation.retryStrategy = { failureCount in
return failureCount < 3
}
您可以为retryStrategy
提供一个自定义的块。在这里,操作将运行3次,如果最终失败才会停止。
这里提供了2个StrategyBlocks
RetryStrategy.none
RetryStrategy.repeat(times: Int)
示例
请参阅包含的测试以获取更多示例。还可以查看广泛使用PeakOperation的PeakNetwork。
入门
安装
- 使用Cocoapods,将以下命令添加到Podfile中:
pod 'PeakOperation'
。 - 在需要的地方导入
import PeakOperation
。
贡献
请阅读CONTRIBUTING.md以了解我们的行为守则以及向我们的pull请求提交的过程。
版本管理
我们使用SemVer进行版本管理。
许可证
本项目受MIT许可证许可 - 请参阅LICENSE.md文件以获取详细信息。
致谢
Peak框架
Peak框架是由3Squared团队创建的一系列开源微框架集合,以其命名的名字Peak District。它由以下组成:
名称 | 描述 |
---|---|
PeakCoreData | 为Core Data 提供增强和便利。 |
PeakNetwork | 一个基于Session 和PeakOperation构建的重联网件框架,利用了Codable 的强大功能。 |