3

I found that as predicted when I was writing an image to file that my UI was blocked for the duration, which was not acceptable. When I write the image to file I then post an NS Notification so that I can do some other specific jobs related to that completion. Original working but UI blocking code:

-(void)saveImageToFile {
    NSString *imagePath = [self photoFilePath];
    BOOL jpgData = [UIImageJPEGRepresentation([[self captureManager] stillImage], 0.5) writeToFile:imagePath atomically:YES];

if (jpgData) {        
    [[NSNotificationCenter defaultCenter] postNotificationName:kImageSavedSuccessfully object:self];
}

To avoid the UI blocking I have put the writeToFile: into a Grand Central Dispatch queue so it runs as a concurrent thread. But when the write is completed and the thread is done, I want to post an NSNotification. I cannot as the code is shown here because it is in a background thread. But that is the functionality I want to accomplish, realizing this is not workable code:

-(void)saveImageToFile {
    NSString *imagePath = [self photoFilePath];

    // execute save to disk as a background thread
    dispatch_queue_t myQueue = dispatch_queue_create("com.wilddogapps.myqueue", 0);
    dispatch_async(myQueue, ^{
        BOOL jpgData = [UIImageJPEGRepresentation([[self captureManager] stillImage], 0.5) writeToFile:imagePath atomically:YES];
        dispatch_async(dispatch_get_main_queue(), ^{
            if (jpgData) {        
            [[NSNotificationCenter defaultCenter] postNotificationName:kImageSavedSuccessfully object:self];
            }
        });
    });
}

What is the correct mechanism here to post this notification to gain the functionality I want ?

Ric
  • 796
  • 12
  • 28

2 Answers2

6

A couple possibilities here.

1)

How about [NSObject performSelectorOnMainThread: ...] ?

E.G.

-(void) doNotification: (id) thingToPassAlong
{
    [[NSNotificationCenter defaultCenter] postNotificationName:kImageSavedSuccessfully object:thingToPassAlong];
}

-(void)saveImageToFile {
    NSString *imagePath = [self photoFilePath];

    // execute save to disk as a background thread
    dispatch_queue_t myQueue = dispatch_queue_create("com.wilddogapps.myqueue", 0);
    dispatch_async(myQueue, ^{
        BOOL jpgData = [UIImageJPEGRepresentation([[self captureManager] stillImage], 0.5) writeToFile:imagePath atomically:YES];
        dispatch_async(dispatch_get_main_queue(), ^{
            if (jpgData) {   
                [self performSelectorOnMainThread: @selector(doNotification:) withObject: self waitUntilDone: YES];
            }
        });
    });
}

More details at http://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html#//apple_ref/occ/instm/NSObject/performSelectorOnMainThread:withObject:waitUntilDone:

or 2)

Completion Callbacks

as seen at How can I be notified when a dispatch_async task is complete?

Community
  • 1
  • 1
Michael Dautermann
  • 88,797
  • 17
  • 166
  • 215
  • I think the simplicity of your first option is amazingly clean and clear. I was not aware of performSelectorOnMainThread:. What a great way to point out of the sub-thread and perform work in the main loop. Very good. Thx. – Ric Oct 06 '11 at 07:53
  • Does the code in the original question not work? It looks like it should function as intended, since you're dispatching the notification to the main queue at the end of the block passed to `myQueue`. I prefer this pattern over using `performSelector:onMainThread:` since you're not writing a separate method just to handle your notification. Cleaner code IMO. – sho Oct 28 '11 at 03:33
  • So Im confused now with all the options. NSNotificationCenter, GrandCentralDispatch-Blocks & performSelectorOnMainThread. I have used pSOMT with an NSOperation & Queue, but never the first two. Can someone please explain the differences or advantages? – marciokoko Feb 01 '12 at 16:22
  • 2
    your call to performSelectorOnMainThread is superfluous. dispatch_async(dispatch_get_main_queue(),... already put you on the main thread. the important part tho being the postNotificationName:object: is what I was looking for tho, so good show. Thanks :) – The Lazy Coder Sep 25 '12 at 20:04
  • Saved my day! The performSelectorOnMainThread trick is awesomeness when having spent so many hours banging my head against this thing! – Fabio Russo May 08 '13 at 16:02
1
-(void)saveImageToFile {
    NSString *imagePath = [self photoFilePath];

    // execute save to disk as a background thread
    dispatch_queue_t myQueue = dispatch_queue_create("com.wilddogapps.myqueue", 0);
    dispatch_async(myQueue, ^{
        BOOL jpgData = [UIImageJPEGRepresentation([[self captureManager] stillImage], 0.5) writeToFile:imagePath atomically:YES];
        dispatch_async(dispatch_get_main_queue(), ^{
            if (jpgData) {        
            [[NSNotificationCenter defaultCenter] postNotificationName:kImageSavedSuccessfully object:self];
            }
        });
    });
}

That is already correct. However why do you need to use notification if you already dispatch_get_main_queue()?

Just use

        dispatch_async(dispatch_get_main_queue(), ^{
            if (jpgData) {        
            //Do whatever you want with the image here
            }
        });

Anyway your original solution is fine. It's not blocking. Basically it'll save the file at other thread and once it's done it'll do what it takes.

What ever you do will be done on the same thread with the thread that call [[NSNotificationCenter defaultCenter] postNotificationName:kImageSavedSuccessfully object:self]; namely main thread.

user4951
  • 32,206
  • 53
  • 172
  • 282