我有以下(伪)代码:
- (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也在主线程上。
发布于 2013-07-29 17:59:06
如果您的完成块也在主线程上被调用,则可能很难实现此,因为在执行完成块之前,您的方法需要返回。您应该将异步方法的实现更改为:
或者
0
初始化一个信号量,然后在主线程上调用wait
,并在完成时调用signal
。在任何情况下,阻塞主线程在图形用户界面应用程序中是非常糟糕的想法,但这不是你问题的一部分。在测试、命令行工具或其他特殊情况下,可能需要阻塞主线程。在这种情况下,请进一步阅读:
如何在主线程上等待主线程回调:
有一种方法可以做到这一点,但可能会产生意想不到的后果。谨慎行事!
主线程是特殊的。它运行的是同时处理+[NSOperationQueue mainQueue]
和dispatch_get_main_queue()
的+[NSRunLoop mainRunLoop]
。分派到这些队列的所有操作或块都将在主运行循环内执行。这意味着,这些方法可以采用任何方法来调度完成块,这在所有这些情况下都应该有效。这就是它:
__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");
这两个布尔值是为了确保块立即同步完成的情况下的一致性。
如果-performOperationWithCompletionOnMainQueue:
为asynchronous,,则输出将为:
启动
等待..。
完成!
继续
如果该方法为synchronous,,则输出将为:
启动
完成!
继续
什么是魔法?调用CFRunLoopRun()
不会立即返回,只有在调用CFRunLoopStop()
时才会返回。此代码位于主RunLoop上,因此再次运行主RunLoop将恢复所有调度块、计时器、套接字等的执行。
Warning:可能的问题是,所有其他计划的计时器和块将同时执行。此外,如果从不调用完成块,您的代码将永远不会到达Continue
日志。
您可以将此逻辑包装在一个对象中,这样可以更容易地重复使用此模式:
@interface MYRunLoopSemaphore : NSObject
- (BOOL)wait;
- (BOOL)signal;
@end
因此,代码将简化为:
MYRunLoopSemaphore *semaphore = [MYRunLoopSemaphore new];
[self performOperationWithCompletionOnMainQueue:^{
[semaphore signal];
}];
[semaphore wait];
发布于 2013-10-29 15:45:15
我认为Mike Ash (http://www.mikeash.com/pyblog/friday-qa-2013-08-16-lets-build-dispatch-groups.html的答案就是“等待几个线程完成,然后在所有线程完成后再做一些事情”。更好的是,您甚至可以使用分派组同步或非同步地等待。
从Mike Ash的博客中复制并修改了一个简短的例子:
dispatch_group_t group = dispatch_group_create();
for(int i = 0; i < 100; i++)
{
dispatch_group_enter(group);
DoAsyncWorkWithCompletionBlock(^{
// Async work has been completed, this must be executed on a different thread than the main thread
dispatch_group_leave(group);
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
或者,您可以-同步等待并在所有块完成时执行操作,而不是dispatch_group_wait:
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
UpdateUI();
});
发布于 2013-07-29 17:27:00
int i = 0;
//the below code goes instead of for loop
NSString *name = [names objectAtIndex:i];
[someObject lookupName:name completion:^(NSString* urlString)
{
// A. Something that takes a few seconds to complete.
// B.
i+= 1;
[self doSomethingWithObjectInArray:names atIndex:i];
}];
/* add this method to your class */
-(void)doSomethingWithObjectInArray:(NSArray*)names atIndex:(int)i {
if (i == names.count) {
// C.
}
else {
NSString *nextName = [names objectAtIndex:i];
[someObject lookupName:nextName completion:^(NSString* urlString)
{
// A. Something that takes a few seconds to complete.
// B.
[self doSomethingWithObjectInArray:names atIndex:i+1];
}];
}
}
我只是在这里输入了代码,所以一些方法的名称可能会拼写错误。
https://stackoverflow.com/questions/17920169
复制相似问题