Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p><strong>If your completion block is also called on the Main Thread, it might be difficult to achieve this</strong>, because before the completion block can execute, your method need to return. You should change implementation of the asynchronous method to:</p> <ol> <li>Be synchronous.<br> or</li> <li>Use other thread/queue for completion. Then you can use Dispatch Semaphores for waiting. You initialize a semaphore with value <code>0</code>, then call <code>wait</code> on main thread and <code>signal</code> in completion.</li> </ol> <p>In any case, <strong>blocking Main Thread is very bad idea</strong> in GUI applications, but that wasn't part of your question. Blocking Main Thread may be required in tests, in command-line tools, or other special cases. In that case, read further:</p> <hr> <h3>How to wait for Main Thread callback <em>on</em> the Main Thread:</h3> <p>There is a way to do it, but could have unexpected consequences. <em>Proceed with caution!</em></p> <p>Main Thread is special. It runs <code>+[NSRunLoop mainRunLoop]</code> which handles also <code>+[NSOperationQueue mainQueue]</code> and <code>dispatch_get_main_queue()</code>. All operations or blocks dispatched to these queues will be executed within the Main Run Loop. This means, that the methods may take <em>any</em> approach to scheduling the completion block, this should work in all those cases. Here it is:</p> <pre><code>__block BOOL isRunLoopNested = NO; __block BOOL isOperationCompleted = NO; NSLog(@"Start"); [self performOperationWithCompletionOnMainQueue:^{ NSLog(@"Completed!"); isOperationCompleted = YES; if (isRunLoopNested) { CFRunLoopStop(CFRunLoopGetCurrent()); // CFRunLoopRun() returns } }]; if ( ! isOperationCompleted) { isRunLoopNested = YES; NSLog(@"Waiting..."); CFRunLoopRun(); // Magic! isRunLoopNested = NO; } NSLog(@"Continue"); </code></pre> <p>Those two booleans are to ensure consistency in case of the block finished synchronously immediately.</p> <p>In case the <code>-performOperationWithCompletionOnMainQueue:</code> is <strong>asynchronous</strong>, the output would be:</p> <blockquote> <p>Start<br> Waiting...<br> Completed!<br> Continue </p> </blockquote> <p>In case the method is <strong>synchronous</strong>, the output would be:</p> <blockquote> <p>Start<br> Completed!<br> Continue </p> </blockquote> <p>What is the <em>Magic</em>? Calling <code>CFRunLoopRun()</code> doesn’t return immediately, but only when <code>CFRunLoopStop()</code> is called. This code <em>is</em> on Main RunLoop so running the Main RunLoop <em>again</em> will resume execution of all scheduled block, timers, sockets and so on.</p> <p><strong>Warning:</strong> The possible problem is, that all other scheduled timers and block will be executed in meantime. Also, if the completion block is never called, your code will never reach <code>Continue</code> log.</p> <p>You could wrap this logic in an object, that would make easier to use this pattern repeatedy:</p> <pre><code>@interface MYRunLoopSemaphore : NSObject - (BOOL)wait; - (BOOL)signal; @end </code></pre> <p>So the code would be simplified to this:</p> <pre><code>MYRunLoopSemaphore *semaphore = [MYRunLoopSemaphore new]; [self performOperationWithCompletionOnMainQueue:^{ [semaphore signal]; }]; [semaphore wait]; </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload