2013-03-03 31 views
2

我正在编写一个应用程序,它将从URL中获取多个图像,将它们转换为UIImage,然后将它们添加到照片库,然后添加到自定义相册。我不相信有可能将它们添加到自定义相册中,而不会将其添加到相机胶卷中,因此我认为它是不可能的(但如果可能,这将是理想的)。在iOS 6(自定义相册)中快速保存多个图像

我的问题是,我使用this site的代码,它确实有效,但是一旦它处理了较大的照片,它就会返回一些'Write Busy'。如果我将函数复制到自己的完成代码中,然后再次在下一个代码中等等,直到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个左右,并且可能分辨率非常高,所以我需要一些能够可靠地反复执行此操作而不会丢失多个图像的东西。

感谢 罗布

回答

0

如果saveImage:toAlbum:withCompletionBlock它使用dispatch_async我担心的I/O操作过多线程产卵:您触发每个写任务是由前一个阻塞(bacause还在做着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循环的代码一样,所以当信号量在等待主线程不被阻塞时。

+0

感谢您的回答,不幸的是我不熟悉信号量,但我会做一些阅读,因为它们看起来很有用。我只在一周前开始! – 2013-03-04 19:57:15

+0

这是一个很好的链接,我认为http://mikeash.com/pyblog/friday-qa-2009-09-25-gcd-practicum.html但它有点高级。 – Ultrakorne 2013-03-04 21:53:22

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的其余部分可被包含在功能和可以被监视的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测试)工作,并应导致所有图片被正确保存没有错误。