EXIF 的 PHContentEditingInputRequestOptions 阻塞主线程

Nic*_*pes 1 multithreading exif grand-central-dispatch ios swift

我正在获取图像 EXIF 以获取 LensMake。

这是我发现这样做的唯一方法:

let lstAssets : PHFetchResult<PHAsset> = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: nil)
lstAssets.enumerateObjects({object , index, stop in
    let options = PHContentEditingInputRequestOptions()
    object.requestContentEditingInput(with: options) { (contentEditingInput: PHContentEditingInput?, _) -> Void in
        let fullImage : CIImage = CIImage(contentsOf: contentEditingInput!.fullSizeImageURL!)!
        if let exif = fullImage.properties["{Exif}"]{ ...
Run Code Online (Sandbox Code Playgroud)

问题在于 requestContentEditingInput 的回调/声明/完成中的代码在主线程中运行,并阻止来自 UI 的任何调用(例如按钮的 IBAction)。因此,如果我以 100 的 lstAssets.count 开始并在 enumerateObjects 中发出 100 个请求,则需要完成所有回调才能在 UI 中执行某些操作。

我尝试将 enumerateObjects 放在 DispatchQueue.global(qos: .background).async 中,但没有成功。如果我将回调代码放在后台线程中,也会发生同样的情况。

奇怪的是,如果我将 PHImageManager.default() 与 requestImageData 用于图像,则这种行为会有所不同,但问题是我无法以这种方式获得 EXIF。

如何解除阻塞队列?有没有更好的方法来获得EXIF?

我在 Xcode 8.2 中运行 Swift 3。

更新:GCD 的奇怪行为:在 PHContentEditingInputRequestOptions 解除主线程阻塞之前,就在 enumerateObjects 内部运行 requestImageData,但是当通过单击进入下一个屏幕时,UI 仍会等待回调结束。

mar*_*aie 5

如果您只需要读取 exif 元数据,您可以使用PHImageManager它对后台队列更友好。因此,您可以将代码更改为如下所示:

let operationQueue = OperationQueue()

let lstAssets : PHFetchResult<PHAsset> = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: nil)
operationQueue.addOperation {
    self.readPhotosMetadata(result: lstAssets, operationQueue: operationQueue)
}

func readPhotosMetadata(result: PHFetchResult<PHAsset>, operationQueue: OperationQueue) {
    let imageManager = PHImageManager.default()
    result.enumerateObjects({object , index, stop in
        let options = PHImageRequestOptions()
        options.isNetworkAccessAllowed = true
        options.isSynchronous = false
        imageManager.requestImageData(for: object, options: options, resultHandler: { (imageData, dataUTI, orientation, info) in
            operationQueue.addOperation {
                guard let data = imageData,
                    let metadata = type(of: self).fetchPhotoMetadata(data: data) else {
                        print("metadata not found for \(object)")
                        return
                }
                print(metadata)
            }
        })
    })
}

static func fetchPhotoMetadata(data: Data) -> [String: Any]? {
    guard let selectedImageSourceRef = CGImageSourceCreateWithData(data as CFData, nil),
        let imagePropertiesDictionary = CGImageSourceCopyPropertiesAtIndex(selectedImageSourceRef, 0, nil) as? [String: Any] else {
        return nil
    }
    return imagePropertiesDictionary

}
Run Code Online (Sandbox Code Playgroud)