我使用Operation
和OperationQueue
来下载一系列视频。这是我的下载Operation
。
import UIKit
import AVFoundation
class VideoDownloader: Operation {
let videoRecord: VideoRecord
init(videoRecord: VideoRecord) {
self.videoRecord = videoRecord
}
override func main() {
if isCancelled {
return
}
let asset = AVURLAsset(url: videoRecord.url)
asset.resourceLoader.setDelegate(self, queue: DispatchQueue.main)
let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality)
let filename = "\(videoRecord.id).mp4"
let documentsDirectory = FileManager.default.urls(for: FileManager.SearchPathDirectory.documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).last!
let outputURL = documentsDirectory.appendingPathComponent(filename)
exporter?.outputURL = outputURL
exporter?.outputFileType = .mp4
exporter?.exportAsynchronously {
if let error = exporter?.error {
print(error)
} else {
print(outputURL)
}
}
}
}
extension VideoDownloader: AVAssetResourceLoaderDelegate {
}
这很好用。但我对下面的一行表示担忧。
asset.resourceLoader.setDelegate(self, queue: DispatchQueue.main)
目前我正在主线程上设置委托。我不认为这是正确的,因为它在后台线程中运行。我不确定,如果我在这里错了,请纠正我。
我认为如果我能将它的队列设置为当前正在运行的队列,那就最好了。但是如何从操作类中访问队列?
答案 0 :(得分:0)
在你的main()中你正在使用异步API,比如AVAssetExportSession和AVResourceLoaderDelegate,你应该实现一个异步操作(Apple有关于此的文档)。您必须更改某些内部属性的状态并调用willChangeValueForKey和didChangeValueForKey,以便在异步操作完成时通知OperationQueue。如果你不这样做,OperationQueue会相信你的操作将在main()方法返回后立即完成。 OperationQueue将从队列中删除操作,并且不会遵守maxConcurrentOperations,因为实际上您的操作仍将在引擎盖下运行。
因为您使用的是非阻塞且可能在各种线程中运行的异步API,所以我建议您只创建自己的后台队列或使用AVResourceLoaderDelegate的现有全局队列。只需保持OperationQueue的内部队列,它就可以作为一个简单的同步层。