17

我正在尝试使用PHAssetChangeRequestiOS 8 中的新类将一大批照片保存到照片库中。问题是,看起来保存照片的守护进程本身因大量照片而意外崩溃(我是尝试大约 500)。任何人都知道如何绕过这个限制?这是守护进程本身的内存使用问题吗?它也可能是更改块的超时限制,因为在下面的前 2 个日志语句之间存在小的差距。

守护进程不应该已经考虑了这个用例,因为新照片框架中的超级复杂模型和设计应该能够处理assetsd这样的事情吗?文档样本本身展示了保存照片的能力。

这是我的代码示例:

[[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

4 回答 4

2

这个想法是小批量进行。PHPhotoLibrary::performChanges一起提交一次更改请求,因此即使它能够完成,您也无法从代表或块中获取任何有关进度的更新。2017 年 WWDC 会议“照片 API 中的新功能”附带了一个示例应用程序“为测试创建大型照片库”,它正是这样做的。每个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 个左右照片 URL 的方法的方法。我个人不喜欢递归写作。代码可以很容易地更改为非递归样式。

于 2018-02-05T03:49:40.923 回答
1

而不是creationRequestForAssetFromImageAtFileURL,我使用了这种方法,它非常适合 10 张图像(这部分代码在 a 中重复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);
        }
}]; 
于 2015-04-15T09:07:14.040 回答
0

即使在现代 ARC 时代,在设备上处理大量图像时,您也必须非常小心内存管理。我有大量图像要处理(50+ 需要调整大小),最终选择了 CGImageSourceCreateThumbnailAtIndex() ,正如这里的答案所建议的那样。它使用应该非常有效的 ImageIO。我剩下的唯一问题是,由于某种原因,它仍然会挂在内存中,除非我将 for 循环包装在 @autoreleasepool {}

for (ALAsset *asset in assets) {
        @autoreleasepool {
            resizedImage = [self thumbnailForAsset:asset maxPixelSize:JPEG_MAX_DIMENSION];
        // do stuff here
    }
}
于 2015-02-11T22:24:54.690 回答
-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
于 2015-01-09T04:39:18.960 回答