Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
345 views
in Technique[技术] by (71.8m points)

ios - How to wait for method that has completion block (all on main thread)?

I have the following (pseudo) code:

- (void)testAbc
{
    [someThing retrieve:@"foo" completion:^
    {
        NSArray* names = @[@"John", @"Mary", @"Peter", @"Madalena"];
        for (NSString name in names)
        {
            [someObject lookupName:name completion:^(NSString* urlString)
            {
                // A. Something that takes a few seconds to complete.
            }];

            // B. Need to wait here until A is completed.
        }
    }];

    // C. Need to wait here until all iterations above have finished.
    STAssertTrue(...);
}

This code is running on main thread, and also the completion block A is on main thread.

  • How do I wait at B for A to complete?
  • How do subsequently wait at C for the outer completion block to complete?
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

If your completion block is also called on the Main Thread, it might be difficult to achieve this, because before the completion block can execute, your method need to return. You should change implementation of the asynchronous method to:

  1. Be synchronous.
    or
  2. Use other thread/queue for completion. Then you can use Dispatch Semaphores for waiting. You initialize a semaphore with value 0, then call wait on main thread and signal in completion.

In any case, blocking Main Thread is very bad idea 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:


How to wait for Main Thread callback on the Main Thread:

There is a way to do it, but could have unexpected consequences. Proceed with caution!

Main Thread is special. It runs +[NSRunLoop mainRunLoop] which handles also +[NSOperationQueue mainQueue] and dispatch_get_main_queue(). All operations or blocks dispatched to these queues will be executed within the Main Run Loop. This means, that the methods may take any approach to scheduling the completion block, this should work in all those cases. Here it is:

__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");

Those two booleans are to ensure consistency in case of the block finished synchronously immediately.

In case the -performOperationWithCompletionOnMainQueue: is asynchronous, the output would be:

Start
Waiting...
Completed!
Continue

In case the method is synchronous, the output would be:

Start
Completed!
Continue

What is the Magic? Calling CFRunLoopRun() doesn’t return immediately, but only when CFRunLoopStop() is called. This code is on Main RunLoop so running the Main RunLoop again will resume execution of all scheduled block, timers, sockets and so on.

Warning: 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 Continue log.

You could wrap this logic in an object, that would make easier to use this pattern repeatedy:

@interface MYRunLoopSemaphore : NSObject

- (BOOL)wait;
- (BOOL)signal;

@end

So the code would be simplified to this:

MYRunLoopSemaphore *semaphore = [MYRunLoopSemaphore new];
[self performOperationWithCompletionOnMainQueue:^{
    [semaphore signal];
}];
[semaphore wait];

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...