2012-08-27 77 views
1

更新:这里也报道此问题,原因的更详细的治疗:的Objective-C:UIImageWriteToSavedPhotosAlbum()+异步=问题

UIImageWriteToSavedPhotosAlbum saves only 5 image out of 10. Why?

在我的情况下,该错误是:“写繁​​忙” - 这似乎是与设备速度有关的问题。可能有一些解决方案需要手动处理线程或类似的东西 - 但是,在Tommy的回答下面,我序列化了保存图像,并且解决了这个问题。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

现状:

我试图使用for-loop(图像数量取决于用户输入 - 可能是1,2,3或理论上数百)保存图像的大集合到相机卷。 NSSet指向图像(尽管我可以很容易地执行NSArray),并且在for循环完成后NSSet设置为nil

我使用UIImageWriteToSavedPhotosAlbum()要保存在分离的线程的图像(使用NSThreaddetachNewThreadSelector:toTarget:withObject:类方法),并启动主线程上UIActivityIndicator微调。

问题:

当我试图挽救了超过〜5,〜5日将输出此错误日志中之后的任何图像更:

*** -[NSKeyedUnarchiver initForReadingWithData:]: data is NULL

例如,让我们说我导出了9张图片。 for循环将遍历所有9个图像(由NSLogging确认),但我会绕过上述错误的4次迭代(并且只有5个图像保存到相机胶卷中)。

但是,如果我在循环中添加一个断点并在每次迭代之间等待一两秒钟,它们全部都会正确保存而不会有任何抱怨。所以..

理论:

根据我的记录和观察,UIImageWriteToSavedPhotosAlbum()显然是异步运行,在某种程度上“太慢”跟上我的应用程序。

有没有一种简单的方法来强制它同步运行(理想的主线程)?我一直在试图为我想要保存的图像添加引用计数,但是(1)这感觉很拙劣,并且(2)我没有解决问题。

任何建议将是伟大的。谢谢!

+0

锁定主线程意味着你的活动指示灯不会动画。你的应用程序会挂起。 – danielbeard

+1

是的,'UIImageWriteToSavedPhotosAlbum'以异步方式运行。错误日志听起来像是要保存的图像为空,请检查图像数据。 – fannheyward

+0

@danielbeard - 是的,正确的,对不起 - 在**情况的第二段中澄清了我的线程** – toblerpwn

回答

3

如果你不想一次写多个图像,那么你可以使用UIImageWriteToSavedPhotosAlbum的完成目标和选择器来实现尾递归的一种形式。

- (void)writeSetToSavedPhotosAlbum:(NSMutableSet *)images 
{ 
    if(![images count]) return; 

    UIImage *imageToSave = [[[images anyObject] retain] autorelease]; 
    [images removeObject:imageToSave]; 
    NSLog(@"I shall now write image %@", imageToSave); 

    UIImageWriteToSavedPhotosAlbum(
      imageToSave, 
      self, 
      @selector(writeSetToSavedPhotosAlbum:), 
      images); 
} 

编辑:喜欢的东西,也可能是值得看你是否得到相同的结果与ALAssetsLibrary-writeImageToSavedPhotosAlbum:orientation:completionBlock:,这需要一个块完成这样就更简单的工作。

+0

我会试验这一点,但只是为了确认:**我百分之百写好所有一次**的图像。你的回答暗示可能有办法做到这一点?这个循环只是我的实现,用于将所有图像传递到'UIImageWriteToSavedPhotosAlbum()'.. – toblerpwn

+0

对不起,我的意思是不要一次写多个图像,即使它不希望“UIImageWriteToSavedPhotosAlbum”平行工作。所以尾递归答案我给部队一个串行转换和写无论。所以如果Apple的代码中存在某种问题,这将是一种解决方法。我不知道任何方法将一组或一系列图像直接传递给资产库,并要求它根据其优先级调度来编写所有这些方法。 – Tommy

+0

我已经使用类似于您的方法来解决问题或多或少(我将用细节更新问题)。它现在对我的活动指示器造成严重破坏,因为在写出实际图像之前主线程已经“完成”,但是现在我们正在解决一个单独的问题,我可以解决这个问题。:) – toblerpwn

0

文档确实提到你会被异步通知。我想知道这是否可行:对于要保存的每个图像创建一个块操作并将其添加到主操作队列中。有点像:

for (UIImage *toSave in imagesToSave) 
{ 
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 
     UIImageWriteToSavedPhotosAlbum(toSave, target, selector, ctx); 
    }]; 
} 

可能值得一试,让我知道它是否有帮助!

+0

好的想法,卡尔,但我的企图没有骰子(在几个地方测试过).. – toblerpwn

+0

是的,回想起来这可能不是一个好主意 - 但也许实现一些并发的NSOperation子类可以很好地完成这项任务。 –

+0

很难说 - 似乎苹果的方法(或者它在这种情况下是一种功能?)只是有自己的想法,不幸的是,心灵只能一次处理几个请求:)在我的应用程序的下一个版本,我将试验这一点(并行运行串行设置 - 如果可行,请尝试3 - 如果可行,请尝试4等),但我最好在几周后离开从那:)在同一时间,串行,一次一个时间节省将不得不做.. – toblerpwn

6

我用ALAssetsLibrary和dispatch_queue,以确保图像保存顺序:

ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; 
dispatch_queue_t queue = dispatch_queue_create("com.myApp.saveToCameraRoll", NULL); 

[images enumerateObjectsUsingBlock:^(UIImage *image, NSUInteger idx, BOOL *stop) { 

    dispatch_async(queue, ^{ 

     dispatch_semaphore_t sema = dispatch_semaphore_create(0); 

     [library writeImageToSavedPhotosAlbum:image.CGImage metadata:metaData completionBlock:^(NSURL *assetURL, NSError *writeError) { 

      if (writeError) { 
       // handle the error 
      } 
      else { 
       if (image == [images lastObject]) { 
        dispatch_async(dispatch_get_main_queue(), ^{ 
         // perhaps indicate to the user that save has finished 
        }); 
       } 
      } 

      dispatch_semaphore_signal(sema); 

     }]; 

     dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); 
     dispatch_release(sema); 
    }); 
}]; 
0

在此代码的工作保存图像:

UIImageWriteToSavedPhotosAlbum([self screenshot], nil, nil, nil); 

UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"Save" message:@"Photo saved to album" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; 
[alert show];