从Operation内部访问队列

时间:2017-11-23 14:07:22

标签: ios swift multithreading nsoperationqueue nsoperation

我使用OperationOperationQueue来下载一系列视频。这是我的下载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)

目前我正在主线程上设置委托。我不认为这是正确的,因为它在后台线程中运行。我不确定,如果我在这里错了,请纠正我。

我认为如果我能将它的队列设置为当前正在运行的队列,那就最好了。但是如何从操作类中访问队列?

1 个答案:

答案 0 :(得分:0)

在你的main()中你正在使用异步API,比如AVAssetExportSession和AVResourceLoaderDelegate,你应该实现一个异步操作(Apple有关于此的文档)。您必须更改某些内部属性的状态并调用willChangeValueForKey和didChangeValueForKey,以便在异步操作完成时通知OperationQueue。如果你不这样做,OperationQueue会相信你的操作将在main()方法返回后立即完成。 OperationQueue将从队列中删除操作,并且不会遵守maxConcurrentOperations,因为实际上您的操作仍将在引擎盖下运行。

因为您使用的是非阻塞且可能在各种线程中运行的异步API,所以我建议您只创建自己的后台队列或使用AVResourceLoaderDelegate的现有全局队列。只需保持OperationQueue的内部队列,它就可以作为一个简单的同步层。