如何等待具有完成块的方法(所有在主线程上)? [英] How to wait for method that has completion block (all on main thread)?

查看:91
本文介绍了如何等待具有完成块的方法(所有在主线程上)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下(伪)代码:

- (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(...);
}

此代码在主线程上运行,完成块A也在主线程。

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


  • 如何在B等待A完成?

  • 后续如何在C等待外部完成块完成?

推荐答案

如果你完成在主线程上也调用了块,可能很难实现这个,因为在完成块可以执行之前,你的方法需要返回。您应该将异步方法的实现更改为:

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. 同步。


  2. 使用其他线程/队列完成。然后您可以使用Dispatch Semaphores进行等待。初始化值为 0 的信号量,然后在主线程和信号上调用 wait 完成。

  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.

无论如何,阻止主线程是个坏主意 GUI应用程序,但这不是您的问题的一部分。在测试,命令行工具或其他特殊情况下可能需要阻止主线程。在这种情况下,请进一步阅读:

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:

有一种方法可以做到这一点,但可能会产生意想不到的后果。 小心谨慎!

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

主线程是特殊的。它运行 + [NSRunLoop mainRunLoop] ,它还处理 + [NSOperationQueue mainQueue] dispatch_get_main_queue( )。分派到这些队列的所有操作或块都将在主运行循环中执行。这意味着,方法可以采用任何方法来调度完成块,这应该适用于所有这些情况。这是:

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.

如果 -performOperationWithCompletionOnMainQueue: 异步,则输出为:


开始

等待...

已完成!

继续

Start
Waiting...
Completed!
Continue

如果方法是同步,则输出为:

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

开始

已完成!

继续

Start
Completed!
Continue

什么是 Magic ?调用 CFRunLoopRun()不会立即返回,但仅在调用 CFRunLoopStop()时才会返回。主RunLoop上的代码,因此再次运行主RunLoop 将继续执行所有预定的块,定时器,套接字等。

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

因此代码将简化为:

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

这篇关于如何等待具有完成块的方法(所有在主线程上)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆