强制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
,但是对我来说这似乎过分了。
话虽如此,我担心这个images
arrays,你在所有的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. });