Backgroundable
Backgroundable 是一组方便的类、扩展和全局函数,用于用 Swift 处理后台处理。
它的主要重点是向现有的 Operation
和 OperationQueue
添加功能,而不向运行时(即速度快)或开发者(即学习得很少)添加开销。
它之所以强大,是因为它简单。
规范
- iOS 10+
- tvOS 10+
- macOS 10.12+
- Swift 4.2+
- 准备就绪的 Objective-C
在后台执行代码
将此转换为此
let queue = OperationQueue()
var bgTaskId = UIBackgroundTaskInvalid
bgTaskId = UIApplication.shared.beginBackgroundTask { () -> Void in
bgTaskId = UIBackgroundTaskInvalid
}
queue.addOperation(BlockOperation(block: { () -> Void in
//do something in the background
UIApplication.shared.endBackgroundTask(bgTaskId)
}))
变为此:
inTheBackground {
//move to the background and get on with your life
}
操作队列
Backgroundable提供了一个方便的方法,用于顺序执行多个操作
var sequentialOperations = [Operation]()
sequentialOperations.append(AsyncOperation({ (op) in
print("Executing sequential operation 1")
op.finish()
}))
sequentialOperations.append(BlockOperation({
print("Executing sequential operation 2")
//The sequential operations work with any Operation objects, not just AsyncOperations
}))
sequentialOperations.append(BlockOperation({ (op) in
print("Executing sequential operation 3")
op.finish()
}))
OperationQueue.background.addSequentialOperations(sequentialOperations, waitUntilFinished: false)
后台队列
Backgroundable还提供了一个全局的后台操作队列(类似于现有OperationQueue.main
)
OperationQueue.background.addOperation {
//do something
}
这是一个BackgroundQueue
类的实例,它自动处理后台任务标识符。每次将操作入列时,都会生成一个后台任务标识符,当队列空时,队列会自动使其失效。
顺序操作将保证一个接一个执行。
后台队列代理
BackgroundQueue
类接受一个BackgroundQueueDelegate
,在队列《backgroundQueueWillStartOperations(_:)》和当《backgroundQueueDidFinishOperations(_:)》时通知。
如果你想要显示网络活动指示器或保存数据库或其他某些内容,这非常方便。天空是无限的!
异步操作
AsyncOperation
是执行异步任务的一种简单方式,其中在OperationQueue
中执行。该类旨在简化在操作队列上执行长时间运行的任务,无论其任务需要在多个线程之间多频繁地跳转。只有当所有事情都完成时,AsyncOperation
才会从队列中移除。
假设我们有一个需要在后台执行的非同步函数
self.loadThingsFromTheInternet(callback: { (result, error) in
//process the result
})
如果我们把这段代码包装在一个Operation
对象中,我们会遇到一个小问题
operationQueue.addOperation(BlockOperation({ [weak self] in
//We're on a background thread now; NICE!
self?.loadThingsFromTheInternet(callback: { (result, error) in
//process the result
//who knows in which thread this function returns...
})
//Aaaand... As soon as we call the load function, the operation will already be finished and removed from the queue
//But we haven't finished what we wanted to do!
//And the queue will now start executing its next operation!
//Sigh...
}))
AsyncOperation
类通过访求操作对象本身来解决这个问题,并在一切完成后只更改它的isFinished
属性
operationQueue.addOperation(AsyncOperation({ [weak self] (op) in
//We're on a background thread now; NICE!
self?.loadThingsFromTheInternet(callback: { (result, error) in
//process the result
//then move to the main thread
onTheMainThread {
//go to the background
inTheBackground {
//do more stuff
//once everything is done, finish
op.finish()
//only now the queue will start working on the next thing
}
}
})
}))
非常好,不是吗?
超时
异步操作(AsyncOperation)无法知道何时完成(因此,在其工作完成后需要调用op.finish())。但是,有时开发者——嗯——会忘记这些事情。
因此,为了应对op.finish()可能永远不会被调用(从而阻碍OperationQueue)的情况,AsyncOperation带有超时功能(默认为10秒)。在超时过后,操作将被自动完成并从队列中移除。
可能存在这样的情况,即你的AsyncOperation的工作负载需要比默认超时更长时间。如果发生这种情况,你可以像这样定义一个新的超时:
AsyncOperation(timeout: 20, { (op) in
//perform very long task
op.finish()
})
可选地,你可以在创建新的AsyncOperation时设置onTimeoutCallback,以便在操作超时时得到通知。
取消
根据Apple的文档[href="https://developer.apple.com/documentation/foundation/operation/1408418-iscancelled"],在执行闭包的过程中,始终检查操作是否已被取消,并在需要时提前短路是一个好主意。例如
AsyncOperation({ (op) in
//do some work
guard !op.isCancelled else { return } //No need to call finish() in this case
//do some more work
})
唯一性策略
唯一性策略决定了具有相同name的AsyncOperation是否可以在BackgroundQueue中同时存在。这对于去重操作非常有用,例如
@IBAction func refresh(_ sender: UIRefreshControl) {
let op = AsyncOperation(name: "Call to API endpoint /xyz", uniquenessPolicy: .drop) { op in
//make the call to the API
op.finish()
}
OperationQueue.background.addOperation(op)
}
第一次用户激活刷新控件时,操作将正常添加到队列中,因为没有其他名为“调用API端点/xyz”的操作。但是,如果用户在第一次调用API返回之前再次激活控件,那么.drop策略将确保不再将第二个操作添加到队列中,因为在其中已经有了一个同名的操作。如果设置了.replace,则取消之前的操作,并替换为新操作。
真酷!
安装
Cocoapods
pod 'Backgroundable', '~> 1.4'
然后,在需要的地方导入import Backgroundable
。
Carthage
github "BellAppLab/Backgroundable" ~> 1.4
然后,在需要的地方导入import Backgroundable
。
Swift包管理器
dependencies: [
.package(url: "https://github.com/BellAppLab/Backgroundable", from: "1.4")
]
然后,在需要的地方导入import Backgroundable
。
Git子模块
cd toYourProjectsFolder
git submodule add -b submodule --name Backgroundable https://github.com/BellAppLab/Backgroundable.git
然后将Backgroundable
文件夹拖放到您的Xcode项目。
作者
Bell App Lab,[email protected]
致谢
许可证
Backgroundable 在 MIT 许可下可用。有关更多信息,请参阅 LICENSE 文件。