正如其他人所指出的,您希望避免在主线程上等待,从而冒死锁的风险。因此,虽然您可以将其推送到全局队列,但另一种方法是使用众多机制中的一种来执行一系列异步任务。选项包括异步Operation 子类或承诺(例如PromiseKit)。
例如,要将图像保存任务包装在异步Operation 中并将它们添加到OperationQueue,您可以像这样定义图像保存操作:
class ImageSaveOperation: AsynchronousOperation {
let image: UIImage
let imageCompletionBlock: ((NSError?) -> Void)?
init(image: UIImage, imageCompletionBlock: ((NSError?) -> Void)? = nil) {
self.image = image
self.imageCompletionBlock = imageCompletionBlock
super.init()
}
override func main() {
UIImageWriteToSavedPhotosAlbum(image, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
}
func image(_ image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer) {
imageCompletionBlock?(error)
complete()
}
}
然后,假设您有一个数组images,即[UIImage],您可以这样做:
let queue = OperationQueue()
queue.name = Bundle.main.bundleIdentifier! + ".imagesave"
queue.maxConcurrentOperationCount = 1
let operations = images.map {
return ImageSaveOperation(image: $0) { error in
if let error = error {
print(error.localizedDescription)
queue.cancelAllOperations()
}
}
}
let completion = BlockOperation {
print("all done")
}
operations.forEach { completion.addDependency($0) }
queue.addOperations(operations, waitUntilFinished: false)
OperationQueue.main.addOperation(completion)
显然,您可以自定义它以在出错时添加重试逻辑,但现在可能不需要这样做,因为“太忙”问题的根源是太多并发保存请求的结果,我们已经消除了这一点。这只留下不太可能通过重试解决的错误,所以我可能不会添加重试逻辑。 (错误更可能是权限失败、空间不足等)但如果您真的需要,您可以添加重试逻辑。更有可能的是,如果您有错误,您可能只想取消队列中所有剩余的操作,就像我上面所说的那样。
注意,上述子类AsynchronousOperation,只是Operation 的子类isAsynchronous 返回true。例如:
/// Asynchronous Operation base class
///
/// This class performs all of the necessary KVN of `isFinished` and
/// `isExecuting` for a concurrent `NSOperation` subclass. So, to developer
/// a concurrent NSOperation subclass, you instead subclass this class which:
///
/// - must override `main()` with the tasks that initiate the asynchronous task;
///
/// - must call `completeOperation()` function when the asynchronous task is done;
///
/// - optionally, periodically check `self.cancelled` status, performing any clean-up
/// necessary and then ensuring that `completeOperation()` is called; or
/// override `cancel` method, calling `super.cancel()` and then cleaning-up
/// and ensuring `completeOperation()` is called.
public class AsynchronousOperation : Operation {
private let syncQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".opsync")
override public var isAsynchronous: Bool { return true }
private var _executing: Bool = false
override private(set) public var isExecuting: Bool {
get {
return syncQueue.sync { _executing }
}
set {
willChangeValue(forKey: "isExecuting")
syncQueue.sync { _executing = newValue }
didChangeValue(forKey: "isExecuting")
}
}
private var _finished: Bool = false
override private(set) public var isFinished: Bool {
get {
return syncQueue.sync { _finished }
}
set {
willChangeValue(forKey: "isFinished")
syncQueue.sync { _finished = newValue }
didChangeValue(forKey: "isFinished")
}
}
/// Complete the operation
///
/// This will result in the appropriate KVN of isFinished and isExecuting
public func complete() {
if isExecuting { isExecuting = false }
if !isFinished { isFinished = true }
}
override public func start() {
if isCancelled {
isFinished = true
return
}
isExecuting = true
main()
}
}
现在,我很欣赏操作队列(或承诺)对于您的情况来说似乎有点矫枉过正,但它是一种有用的模式,您可以在任何有一系列异步任务的地方使用它。有关操作队列的更多信息,请随时参考Concurrency Programming Guide: Operation Queues。