4

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];
         });
});
OWolf
  • 5,012
  • 15
  • 58
  • 93
  • `_someUIImage` and `_imageView.image` referes to `UIImage`. There are no difference to both. – iDev Jan 08 '13 at 05:56

3 Answers3

5

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].

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • Thanks! I am trying to set up the completion as you mentioned. What how can I get the context info? The last argument? UIImageWriteToSavedPhotosAlbum(_imageView.image, self, @selector(dismissHUD), context_info); I'm getting an error = reason: '-[NSInvocation setArgument:atIndex:]: index (2) out of bounds [-1, 1]' – OWolf Jan 08 '13 at 06:07
  • The `contextInfo` point to anything you need it to. Just use NULL if you don't need it. The completion selector has to take 3 arguments, as described in the `UIImageWriteToSavedPhotosAlbum` documentation. It sounds like yours doesn't. It needs to take a `contextInfo` argument, even if you are just passing NULL for that argument. – rob mayoff Jan 08 '13 at 06:31
  • Thanks! If you care to see my solution, or comment, see my answer below. – OWolf Jan 08 '13 at 06:48
  • @robmayoff I'm just trying to understand if calling UIImageWriteToSavedPhotosAlbum() can be done on any thread. So are you saying "just call it on the main thread" because UIImageWriteToSavedPhotosAlbum has a completion selector parameter, or because its a UIKit method and therefore safer to call it on main thread?. I assume the reason why it has a completion selector and target parameter is because it goes off onto its own thread to do the saving, a thread other than the one it was called on? – pnizzle Mar 11 '14 at 05:24
  • 1
    `UIImageWriteToSavedPhotosAlbum` is not documented to be callable from other threads, so you should only call it from the main thread. The fact that it has a completion callback implies that it won't block the main thread. Whether it uses a background thread is an implementation detail and not documented. – rob mayoff Mar 11 '14 at 07:31
3

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

OWolf
  • 5,012
  • 15
  • 58
  • 93
1

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.