phasset.requestContentEditingInput 内存泄漏

Memory leaks on phasset.requestContentEditingInput

我在 requestContentEditingInput 出现内存泄漏。

所以我有一组 PHAssets(图像)。我正在尝试的是通过一张一张地迭代图像来检查图像是否存在元数据 (EXIF),然后我正在编辑该 EXIF 并将该图像保存到磁盘上。阅读内容

 asset.requestContentEditingInput(with: options) { (contentEditingInput, info) in

}

根据仪器,这是导致 15 次泄漏的线路。即使我从块内删除所有内容,调用 requestContentEditingInput 时也会发生泄漏。当我评论这个方法时,我没有看到任何泄漏,这表明泄漏发生在这一行(而不是在它的块内。

完整代码如下:

 let assetWithAlbum = assetsFromPhotoLibrary.remove(at: 0)
    let asset = assetWithAlbum.phAsset
    imageCountRemaining -= 1
    options.isNetworkAccessAllowed = true //Comment out this line to process iCloud Photos last
    asset.requestContentEditingInput(with: options) { (contentEditingInput, info) in
        if info["PHContentEditingInputResultIsInCloudKey"] as? Int == 1 {
            log.ln("Content Editing Input could not be created for asset \(asset.localIdentifier) result is in the Cloud!")/
            NotificationCenter.default.post(name: .contentEditingInputFailed, object: self, userInfo: ["AssetID": asset.localIdentifier])
            return
        }
        guard let safeContentEditingInput = contentEditingInput else {
            log.ln("Content Editing Input could not be created for asset \(asset.localIdentifier)")/
            NotificationCenter.default.post(name: .contentEditingInputFailed, object: self, userInfo: ["AssetID": asset.localIdentifier])
            return
        }
        
        DispatchQueue.global(qos: .userInitiated).async {
            guard let imageURL = safeContentEditingInput.fullSizeImageURL, let fullImage = CIImage(contentsOf: imageURL) else {
               
                NotificationCenter.default.post(name: .contentEditingInputFailed, object: self, userInfo: ["AssetID": asset.localIdentifier])
                return
            }
            var imageProperties = fullImage.properties
            imageProperties.removeValue(forKey: "Orientation")
            guard let exifProperties = imageProperties["{Exif}"] as? [String: Any] else {
           
                NotificationCenter.default.post(name: .containsNoValidEXIFInformation, object: self, userInfo: ["AssetID": asset.localIdentifier])
                return
            }
            

      
            let requestOptions = PHImageRequestOptions()
            //requestOptions.isSynchronous = true //This blocks the calling thread and since we are already on a background thread we are ok
            PHImageManager.default().requestImage(for: asset, targetSize: self.targetSize ?? PHImageManagerMaximumSize, contentMode: .aspectFit, options: requestOptions) { (result, info) in
                guard let imageIsDegraded = info?[PHImageResultIsDegradedKey] as? Bool else {
                  
                    NotificationCenter.default.post(name: .contentEditingInputFailed, object: self, userInfo: ["AssetID": asset.localIdentifier])
                    return
                }
                guard let resultingImage = result else {
                   
                    NotificationCenter.default.post(name: .contentEditingInputFailed, object: self, userInfo: ["AssetID": asset.localIdentifier])
                    return
                }
                if !imageIsDegraded {
                    //Process EXIF information next and store it on a JPG representation of the image
                    guard let jpegImage = resultingImage.jpegData(compressionQuality: 0.85) else {
                      
                        NotificationCenter.default.post(name: .contentEditingInputFailed, object: self, userInfo: ["AssetID": asset.localIdentifier])
                        return
                    }
                    
                    let formattedLocalID = self.format(requestID: asset.localIdentifier)
                    self.saveImageWithProperties(data: jpegImage, filename: formattedLocalID,albumName: assetWithAlbum.albumTitle, properties: imageProperties as NSDictionary) { (jpegWithExif, fileURL) in
                       
                        NotificationCenter.default.post(name: .contentEditingInputComplete, object: fileURL) //Want an observer?
                        self.imageQueue.updateValue(fileURL.absoluteString, forKey: formattedLocalID)
                    }
                }
            }
        }
    }

关于泄漏的任何想法,或者是否有任何其他方法可以访问 PHAsset 图像的 Exif 和内容?

非常感谢!

所以问题是requestContentEditingInput。这是代价高昂的电话。当我遍历一组 PHAssets 图像时,这个函数调用对每个资产索引进行了多次。就我而言,我只需要图像的元数据 (Exif)。如果您只需要图像的元数据,请考虑调用 requestImageDataAndOrientation,它对背景更友好且成本更低。

这是从 PHAsset 获取元数据的方法。

PHImageManager.default().requestImageDataAndOrientation(for: phAsset, options: requestOption,resultHandler: {(data,string,imageOrientation, dictionary) in
            if let data = data{
                
                let metaData = fetchPhotoMetadata(data: data)
                guard let _metaData = metaData else
                {
                    print("No valid data found for the image id =  \(asset.localIdentifier)")/
                    return
                }
                if(!hasValidMetaData(properties: _metaData))
                {
                    print("Image contains no EXIF information and will be removed from queue: \(asset.localIdentifier)")/
                    return
                }
                DispatchQueue.global(qos: .userInitiated).async {
                    //Resize here 
                }
                
            }
        })

 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
}    


 func hasValidMetaData(properties: [String: Any]) -> Bool {
        if(properties["{Exif}"] != nil){
            return true
        }
        return false
    }