使用新的Photos框架保存大量照片?

时间:2014-10-03 16:29:30

标签: ios ios8 photosframework

我正在尝试使用iOS 8中的新PHAssetChangeRequest类将大量照片保存到照片库中。问题是,看起来保存照片的守护程序本身意外地崩溃了大量的照片(我正在尝试大约500张)。任何人都知道如何解决这个限制?这是守护进程本身的内存使用问题吗?它也可能是更改块的超时限制,因为在下面的前两个日志语句之间存在无关紧要的差距。

assetsd守护进程不应该已经考虑到这个用例,因为类似这样的东西几乎就是新的Photos框架中的超复杂模型和设计应该能够处理?文档示例本身展示了保存照片的功能。

这是我的代码示例:

[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
    for (NSURL * url in fileURLs) {
        PHAssetChangeRequest * assetReq = [PHAssetChangeRequest creationRequestForAssetFromImageAtFileURL:url];
    }
    NSLog(@"Added %d assets",fileURLs.count);
} completionHandler:^(BOOL success, NSError *error) {
     if (!success){
         NSLog(@"%@",error);
     }
}];

这就是我的输出:

... Added 501 assets
... Connection to assetsd was interrupted or assetsd died
... Error Domain=NSCocoaErrorDomain Code=-1 "The operation couldn’t be completed. (Cocoa error -1.)

我甚至尝试了performChangesAndWait中的同步PHPhotoLibrary方法,但它也存在同样的问题。

我对建议/想法持开放态度,我被困住了! :(

4 个答案:

答案 0 :(得分:1)

而不是creationRequestForAssetFromImageAtFileURL,我使用了这种方法,它适用于10张图片(这部分代码在tableView:cellForRowAtIndexPath:中重复)

    UIImage *thisImage=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@%@",serverInfo,self.URLs[indexPath.row]]]]];
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
        PHAssetChangeRequest * assetReq = [PHAssetChangeRequest creationRequestForAssetFromImage:thisImage];
        NSLog(@"Added %ld assets",(long)indexPath.row);
    } completionHandler:^(BOOL success, NSError *error) {
        if (!success){
             NSLog(@"%@",error);
        }
}]; 

答案 1 :(得分:1)

这个想法是小批量做的。 PHPhotoLibrary::performChanges一起提交更改请求,因此即使它能够完成,您也无法从委托或块中获取有关进度的任何更新。 2017年WWDC会议"What's New in Photos APIs"附带了一个示例应用程序“Creating Large Photo Libraries for Testing”。每个performChanges提交了10个图像,UI更新来自performChanges的完成块,信号量阻塞线程直到处理完一个批处理。我在这里发布了重要的代码:

private func addPhotos() {
    let batchSize = min(photosToAdd, maxBatchSize)
    if batchSize <= 0 || !active {
        isAddingPhotos = false
        active = false
        return
    }

    workQueue.async {
        let fileURLs = self.generateImagesAndWriteToDisk(batchSize: batchSize)
        self.createPhotoLibraryAssets(with: fileURLs)

        DispatchQueue.main.async {
            self.addPhotos()
        }
    }
}

private func createPhotoLibraryAssets(with imageFileURLs: [URL]) {
    photoLibrarySemaphore.wait() // Wait for any ongoing photo library

    PHPhotoLibrary.shared().performChanges({
        let options = PHAssetResourceCreationOptions()
        options.shouldMoveFile = true
        for url in imageFileURLs {
            let creationRequest = PHAssetCreationRequest.forAsset()
            creationRequest.addResource(with: .photo, fileURL: url, options: options)
        }
    }) { (success, error) in
        if !success {
            print("Error saving asset to library:\(String(describing: error?.localizedDescription))")
        }
        self.photoLibrarySemaphore.signal()
    }
}

以上generateImagesAndWriteToDisk是您需要用您的方法替换以返回一批10张照片网址的方法。我个人不喜欢写递归。代码可以很容易地更改为非递归样式。

答案 2 :(得分:0)

在设备上处理大量图像时,即使在ARC的现代,您也必须非常小心内存管理。我有大量要处理的图像(50+用于调整大小)并最终选择了CGImageSourceCreateThumbnailAtIndex()as suggested by the answer here。它使用ImageIO,它应该是非常有效的。我剩下的唯一问题是,由于某种原因,除非我将我的for循环包装在@autoreleasepool {}

中,否则它仍会依赖于内存
for (ALAsset *asset in assets) {
        @autoreleasepool {
            resizedImage = [self thumbnailForAsset:asset maxPixelSize:JPEG_MAX_DIMENSION];
        // do stuff here
    }
}

答案 3 :(得分:-1)

这不是这个问题的真正解决方案,但它是一种解决方法。您仍然可以使用旧的ALAssetsLibrary将文件成功保存到相机胶卷/照片应用程序。

ALAssetsLibrary* lib = [[ALAssetsLibrary alloc] init];
[lib writeImageDataToSavedPhotosAlbum:imageData metadata:nil
                      completionBlock:^(NSURL *assetURL, NSError *error)
{
    // start saving your next image
}];

在循环浏览要保存的所有照片时,还建议使用AssetsLibrary的单个实例。您可能还想等待第一个图像保存完成以开始保存下一个图像。

然后,您可以根据需要将生成的assetURL转换为PHAsset:

+ (PHFetchResult *)fetchAssetsWithALAssetURLs:(NSArray *)assetURLs
                                  options:(PHFetchOptions *)options