在iOS 6中快速保存多个图像(自定义相册)

时间:2013-03-03 23:11:29

标签: objective-c ios6

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

我的问题是我正在使用this site中的代码并且它确实有效,但是一旦它处理更大的照片,它会返回一些“写忙”。我已经成功地将它们全部保存起来如果我将函数复制到它自己的完成代码中,然后再在下一个中复制,依此类推直到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左右,而且分辨率非常高,所以我需要一些能够可靠地一遍又一遍地执行此操作而不会遗漏多张图像的东西。 / p>

感谢 罗布

2 个答案:

答案 0 :(得分:1)

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

这是我递归使用的代码:

- (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];

此答案适用于使用{6} for iOS 6(在iOS 6.1上测试过)的任何人,并且应该可以正确保存所有图片并且没有错误。

答案 1 :(得分:0)

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

您应该使用信号量同时将写入操作限制为固定数字,或者使用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循环的代码,所以当信号量等待时,主线程不会被阻塞。