强制异步任务按顺序运行

时间:2013-11-28 07:05:06

标签: ios objective-c alassetslibrary

我正在使用ALAssetsLibrary将图片保存到照片库。当它进入循环时,它同时运行并导致内存问题。如何在不引起内存问题的情况下运行

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

3 个答案:

答案 0 :(得分:5)

如果要确保这些写入是连续发生的,可以使用信号量等待图像完成,然后再开始下一次写入:

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数组感到担忧,你在同一时间内将所有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模式(例如尝试从缓存中检索图像;如果不是发现,从持久存储中检索并将其添加到缓存中;在内存压力下,清空缓存),这样您就可以享受在内存中保存图像的性能优势,但可以优雅地处理图像缓存占用过多内存的情况。

答案 1 :(得分:1)

另一种方法是实现“异步循环”:

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:异步方法。

  • 按顺序处理图像列表。

  • 保存所有图像后,将调用完成处理程序。

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

这不是递归方法调用,但是当完成处理程序调用方法saveImages:toAssetLibrary:completion:时,方法已经返回。

可能的改进:

  • 为简洁起见,样本没有错误处理。这应该在实际实现中得到改进。

  • 最好不要使用图片列表,而是最好使用图片的网址列表。

用法

你可以这样使用它:

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

答案 2 :(得分:0)

如果您希望更新使用dispatch_async。

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