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];
}];