2

我正在编写一个应用程序,它将从 URL 中获取几张图像,将它们转换为 UIImage,然后将它们添加到照片库中,然后添加到自定义相册中。我不相信在不将它们放在相机胶卷中的情况下将它们添加到自定义相册中是不可能的,所以我接受它是不可能的(但如果可能的话,那将是理想的)。

我的问题是我正在使用该站点的代码并且它确实有效,但是一旦它处理较大的照片,它就会返回一些“写忙”。如果我将函数复制到它自己的完成代码中,然后再复制到下一个函数中,我已经成功地将它们全部保存,依此类推,直到 6(我看到的最多是 3-4,但我不知道图像,我可以得到一些非常大的图像) - 这导致了一个问题,即它们没有全部包含在自定义相册中,因为它们在这个阶段也出错了,并且没有任何障碍可以让它重复.

我知道实际的图像保存被移动到后台线程(虽然我没有专门设置这个),因为我的代码在错误开始出现之前返回,但理想情况下我需要排队图像以保存在单个背景上线程,因此它们同步发生但不会冻结 UI。

我的代码如下所示:

UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:singleImage]]];
    [self.library saveImage:image toAlbum:@"Test Album" withCompletionBlock:^(NSError *error) {
        if (error!=nil) {
            NSLog(@"Error");
        }
    }];

我已经删除了代码的重复,否则代码示例会很长!它以前是 NSLog 代码存在的地方。

对于我的测试样本,我正在处理 25 张图像,但这可能很容易达到 200 张左右,并且分辨率可能非常高,因此我需要能够可靠地一遍又一遍地执行此操作而不会丢失多张图像的东西。

谢谢罗伯

4

2 回答 2

1

我已经设法通过剥离保存图像代码并将其移动到它自己的函数中使其工作,该函数在对象的数组上递归调用自身,如果它失败,它会将相同的图像重新解析回函数,直到它成功工作完成后将显示“完成”。因为我使用了 completedBlock: from 函数来完成循环,所以每次运行它只运行一个文件保存。

这是我递归使用的代码:

- (void)saveImage {

if(self.thisImage)
{
    [self.library saveImage:self.thisImage toAlbum:@"Test Album" withCompletionBlock:^(NSError *error) {
        if (error!=nil) {
            [self saveImage];
        }
        else
        {
            [self.imageData removeObject:self.singleImageData];
            NSLog(@"Success!");
            self.singleImageData = [self.imageData lastObject];
            self.thisImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:self.singleImageData]]];
            [self saveImage];
        }
    }];
}
else
{
    self.singleImageData = nil;
    self.thisImage = nil;
    self.imageData = nil;
    self.images = nil;
    NSLog(@"Done!");
}

}

为了设置它,我最初使用了一组 UIImages,但这使用了大量内存并且非常慢(我正在测试多达 400 张照片)。我发现一个更好的方法是将 URL 的 NSMutableArray 存储为 NSString,然后在函数中执行 NSData GET。

以下代码是使用数据设置 NSMutableArray 然后调用该函数的代码。它还将第一个 UIImage 设置到内存中并将其存储在 self.thisImage 下:

NSEnumerator *e = [allDataArray objectEnumerator];
NSDictionary *object;

while (object = [e nextObject]) {
    NSArray *imagesArray = [object objectForKey:@"images"];
    NSString *singleImage = [[imagesArray objectAtIndex:0] objectForKey:@"source"];
    [self.imageData addObject:singleImage];
}

self.singleImageData = [self.imageData lastObject];
self.thisImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:self.singleImageData]]];
[self saveImage];

这意味着 UIImage 的其余 getter 可以包含在函数中,并且可以监视 UIImage 的单个实例。我还将原始 URL 记录到 self.singleImageData 中,以便我可以从数组中删除正确的元素以停止重复。

这些是我使用的变量:

self.images = [[NSMutableArray alloc] init];
self.thisImage = [[UIImage alloc] init];
self.imageData = [[NSMutableArray alloc] init];
self.singleImageData = [[NSString alloc] init];

这个答案应该适用于任何使用http://www.touch-code-magazine.com/ios5-saving-photos-in-custom-photo-album-category-for-download/的 iOS 6(在 iOS 6.1 上测试)并且应该导致所有图片都正确保存并且没有错误。

于 2013-03-04T20:06:51.677 回答
0

如果 saveImage:toAlbum:withCompletionBlock 它正在使用 dispatch_async 我担心对于 i/o 操作会产生太多线程:您触发的每个写入任务都被前一个阻塞(因为仍在同一个队列上执行 I/O),所以 gcd将创建一个新线程(通常 global_queue 上的 dispatch_async 由 gcd 通过使用优化的线程数进行优化)。

如果我没记错的话,您应该使用信号量同时将写入操作限制为固定数量,或者使用 iOS 5 提供的 dispatch_io_ 函数。有很多关于如何用这两种方法做到这一点的例子。

一些用于给出想法的即时代码:

 dispatch_semaphore_t aSemaphore = dispatch_semaphore_create(4);
 dispatch_queue_t ioQueue = dispatch_queue_create("com.customqueue", NULL);

 // dispatch the following block to the ioQueue
 // ( for loop with all images )
 dispatch_semaphore_wait(aSemaphore , DISPATCH_TIME_FOREVER);
 [self.library saveImage:image 
                 toAlbum:@"Test Album"
     withCompletionBlock:^(NSError *error){
          dispatch_semaphore_signal(aSemaphore);
     }];

所以每次你最多有 4 个 saveImage:toAlbum,一旦一个完成,另一个就会开始。您必须创建一个自定义队列,就像上面一样(ioQueue)在哪里调度对图像执行 for 循环的代码,因此当信号量等待时,主线程不会被阻塞。

于 2013-03-03T23:52:24.247 回答