OWolf
OWolf

Reputation: 5132

iOS: dispatch_async and UIImageWriteToSavedPhotosAlbum

Just learning how to allocate tasks among threads, or dispatch asynchronously. I understand that any operation that "touches" a view must be done on the main thread. What about: UIImageWriteToSavedPhotosAlbum? I would assume this could be done on a background thread, but am I mistaken?

Also, if it should be done on a background thread, is there a difference between these two calls below, as one saves a UIImage and the other saves a UIImage from a view?

UIImageWriteToSavedPhotosAlbum(_someUIImage ,nil,nil,nil);

UIImageWriteToSavedPhotosAlbum(_imageView.image ,nil,nil,nil);

By the way I am using this setup to run an HUD in the main thread and to tasks in the background, that is my intention.

[HUD_code showMessage:@"saving image"];
dispatch_queue_t concurrentQueue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^{
        UIImageWriteToSavedPhotosAlbum(someUIImage ,nil,nil,nil);
            dispatch_async(dispatch_get_main_queue(), ^{
                [HUD_code dismiss];
         });
});

Upvotes: 4

Views: 4108

Answers (3)

OWolf
OWolf

Reputation: 5132

Here is my latest code after reading the answers, if anyone cares to know, or to comment (appreciated).

-(void)saveToLibrary {

            if (_imageView.image != NULL) {
                messageHUD = @"Saving Image...";
                [SVProgressHUD showWithStatus:messageHUD];
                UIImageWriteToSavedPhotosAlbum(_imageView.image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
            }
    }

- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
    UIAlertView *alert;

    // Unable to save the image
    if (error) {
        alert = [[UIAlertView alloc] initWithTitle:@"Error"
                                           message:@"Unable to save image to Photo Album."
                                          delegate:self cancelButtonTitle:@"Ok"
                                 otherButtonTitles:nil];
    }else {// All is well
        messageHUD = @"Success!\nImage Saved.";
        [SVProgressHUD showSuccessWithStatus:messageHUD];
        [self myPerformBlock:^{[SVProgressHUD dismiss];} afterDelay:0.5];
    }
}

The myPerformBlock is from the following link https://gist.github.com/955123

Upvotes: 3

rob mayoff
rob mayoff

Reputation: 386068

UIKit classes are documented to be usable from the main thread only, except where documented otherwise. (For example, UIFont is documented to be thread-safe.)

There's no explicit blanket statement about the thread safety of UIKit functions (as distinct from classes), so it's not safe to assume they generally thread-safe. The fact that some UIKit functions, like UIGraphicsBeginImageContext, are explicitly documented to be thread-safe, implies that UIKit functions are not generally thread-safe.

Since UIImageWriteToSavedPhotosAlbum can send an asynchronous completion message, you should just call it on the main thread and use its completion support to perform your [HUD_code dismiss].

Upvotes: 5

user529758
user529758

Reputation:

I would assume this could be done on a background thread, but am I mistaken?

Honestly, I would assume it too, since this has absolutely nothing to do with updating the UI, it's just some file operation. However, Apple's documentation says that every call to UIKit needs to be performed on the main thread (Except where something else is explicitly stated). This function is no exception, you have to call it on the main thread.

By the way, this function is asynchronous itself. It will notify the callback object/selector supplied as its 2nd and 3rd arguments when the image is saved, and thus it doesn't block the UI.

Upvotes: 1

Related Questions