强制asynchronous任务按顺序运行

我使用ALAssetsLibrary将图像保存到照片库。 当它进入循环时,它会同时运行并导致内存问题。 我怎样才能运行这不会导致内存问题

 ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; for(UIImage *image in images){ [library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error) { ... // }]; } 

如果要确保这些写操作是连续发生的,则可以在启动下一次写操作之前使用信号灯等待图像的完成:

 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); for (UIImage *image in images) { [library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error) { dispatch_semaphore_signal(semaphore); // signal when done }]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // wait for signal before continuing } 

而且,既然你可能不希望在这个过程中阻塞主队列,你可能想把整个事情分派到一些背景队列中:

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); for (UIImage *image in images) { [library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error) { dispatch_semaphore_signal(semaphore); // signal when done }]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // wait for signal before continuing } }); 

或者,你可以将这个writeImageToSavedPhotosAlbum在一个自定义的NSOperation ,直到完成块才会发布isFinished ,但是对我来说这似乎过分了。


话虽如此,我担心这个imagesarrays,你在所有的UIImage对象同时在内存中。 如果它们很大,或者如果你有很多图像,这可能是有问题的。 通常情况下,您只需简单地维护一组图像名称,然后一次一个实例化图像,例如:

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); for (NSString *path in imagePaths) { @autoreleasepool { ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; UIImage *image = [UIImage imageWithContentsOfFile:path]; [library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error) { dispatch_semaphore_signal(semaphore); // signal when done }]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // wait for signal before continuing } } }); 

通常应该对任何编码模式保持警惕,这种编码模式假定同时在内存中存储大量对象,比如图像。

如果您需要经常访问这些映像,但不是每次都要从永久性存储中重新检索它,则可以使用NSCache模式(例如尝试从caching中检索映像;如果未find,则从永久性存储中检索并将其添加到caching中;在内存压力下,清空caching),这样您就可以享受在内存中保存图像的性能好处,但能够优雅地处理图像caching消耗太多内存的情况。

另一种方法是实现一个“asynchronous循环”:

 typedef void(^completion_t)(id result, NSError* error); - (void) saveImages:(NSMutableArray*)images toAssetLibrary:(ALAssetsLibrary*)library completion:(completion_t)completionHandler { if ([images count] > 0) { UIImage* image = [images firstObject]; [images removeObjectAtIndex:0]; [library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error) { if (error) { } else { } [self saveImages:images toAssetLibrary:library completion:completionHandler]; }]; } else { // finished if (completionHandler) { completionHandler(@"Images saved", nil); } } } 

注意:

  • saveImages:toAssetLibrary:completion:方法是一种asynchronous方法。

  • 按顺序处理图像列表。

  • 完成处理程序将在所有图像被保存时被调用。

为了做到这一点,上面的实现在writeImageToSavedPhotosAlbum:orientation:completionBlock:的完成处理程序中调用自己writeImageToSavedPhotosAlbum:orientation:completionBlock:

但是,这不是一个recursion方法调用:当完成处理程序调用方法saveImages:toAssetLibrary:completion: ,该方法已经返回。

可能的改进:

  • 为简洁起见,示例没有error handling。 这应该在实际中得到改善。

  • 而不是有一个图像列表,你最好使用的URL列表的图像。

用法

你可以像这样使用它:

 ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; [self saveImages:[self.images mutableCopy] toAssetLibrary:library completion:^(id result, NSError*error) { ... }]; 

如果您希望更新使用dispatch_async。

 // This will wait to finish dispatch_async(dispatch_get_main_queue(), ^{ // Update the UI on the main thread. });