Question

I have a method that I add to a GCD queue that I have created (so it's a serial queue) and then run it async. From within that block of code I make a dispatch to the main queue, when that block of code dispatched to the main queue is complete I set a BOOL flag to YES, so that I further down in my code can check if this condition is YES then I can continue to the next method. Here is the code in short:

dispatch_queue_t queue = dispatch_queue_create("ProcessSerialQueue", 0);

dispatch_async(queue, ^{

        Singleton *s = [Singleton sharedInstance];

        dispatch_sync(dispatch_get_main_queue(), ^{
            [s processWithCompletionBlock:^{

                // Process is complete
                processComplete = YES;
            }];
        });
});

while (!processComplete) {

        NSLog(@"Waiting");
}

NSLog(@"Ready for next step");

However this does not work, because dispatch_sync is never able to run the code on the main queue. Is this because I'm running a while loop on the main queue (rendering it busy)?

However if I change the implementation of the while loop to this:

while (!processComplete) {

        NSLog(@"Waiting")
        NSDate *date = [NSDate distantFuture];
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:date];
}

It works without a glitch. Is this an acceptable solution for this scenario? Can I do it any other preferred way? What kind of magic stuff does NSRunLoop do? I need to understand this better.

Was it helpful?

Solution

Part of the main thread's NSRunLoop job is to run any blocks queued on the main thread. By spinning in a while-loop, you're preventing the runloop from progressing, so the queued blocks are never run unless you explicitly make the loop run yourself.

Runloops are a fundemental part of Cocoa, and the documentation is pretty good, so I'd reccommend reading it.

As a rule, I'd avoid manually invoking the runloop as you're doing. You'll waste memory and make make things complicated very quickly if you have multiple manual invocations running on top of one another.

However, there is a much better way of doing this. Split your method into a -process and a -didProcess method. Start the async operation with your -process method, and when it completes, call -didProcess from the completion block. If you need to pass variables from one method to the other, you can pass them as arguments to your -didProcess method.

Eg:

dispatch_queue_t queue = dispatch_queue_create("ProcessSerialQueue", 0);

dispatch_async(queue, ^{
        Singleton *s = [Singleton sharedInstance];

        dispatch_sync(dispatch_get_main_queue(), ^{
            [s processWithCompletionBlock:^{
                [self didProcess];
            }];
        });
});

You might also consider making your singleton own the dispatch queue and make it responsible for handling the dispatch_async stuff, as it'll save on all those nasty embedded blocks if you're always using it asynchronously.

Eg:

[[Singleton sharedInstance] processAyncWithCompletionBlock:^{
   NSLog(@"Ready for next step...");
   [self didProcess];
}];

OTHER TIPS

Doing something like what you posted will most likely freeze the UI. Rather than freezing up everything, call your "next step" code in a completion block.

Example:

dispatch_queue_t queue = dispatch_queue_create("ProcessSerialQueue", 0);
dispatch_queue_t main = dispatch_get_main_queue();

dispatch_async(queue, ^{

        Singleton *s = [Singleton sharedInstance];

        dispatch_async(dispatch_get_main_queue(), ^{
            [s processWithCompletionBlock:^{
                // Next step code
            }];
        });
});

Don't go creating a loop like that waiting for a value inside a block, variables in blocks are read only, instead call your completion code from inside the block.

dispatch_async(queue, ^{
    Singleton *s = [Singelton sharedInstance];
    [s processWithCompletionBlock:^{
        //process is complete
        dispatch_sync(dispatch_get_main_queue(), ^{
            //do something on main queue....
            NSLog(@"Ready for next step");
        });
    }];
});
NSLog(@"waiting");
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top