2012-12-11 135 views
0

我有一个方法,我添加到我创建的GCD队列(所以它是一个串行队列),然后运行它的异步。从那段代码中,我派遣到主队列,当派发到主队列的代码块完成时,我将一个BOOL标志设置为YES,这样我在代码中进一步下来可以检查这个条件是否为YES那么我可以继续下一个方法。下面是短代码:等待的条件继续

dispatch_queue_t queue = dispatch_queue_create("ProcessSerialQueue", 0); 

dispatch_async(queue, ^{ 

     Singleton *s = [Singleton sharedInstance]; 

     dispatch_sync(dispatch_get_main_queue(), ^{ 
      [s processWithCompletionBlock:^{ 

       // Process is complete 
       processComplete = YES; 
      }]; 
     }); 
}); 

while (!processComplete) { 

     NSLog(@"Waiting"); 
} 

NSLog(@"Ready for next step"); 

然而,这是不行的,因为dispatch_sync从来都不是能够在主队列运行代码。这是因为我在主队列上运行一个while循环(使其处于繁忙状态)?

但是,如果我改变而循环的实现这样:

while (!processComplete) { 

     NSLog(@"Waiting") 
     NSDate *date = [NSDate distantFuture]; 
     [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:date]; 
} 

它工作无故障。这是这种情况下可接受的解决方案吗?我可以做任何其他首选的方式吗? NSRunLoop做什么样的魔术?我需要更好地理解这一点。

回答

2

主线程的NSRunLoop作业的一部分是运行在主线程上排队的任何块。通过在while循环中旋转,可以防止runloop的进展,所以排队的块永远不会运行,除非您自己明确地让循环运行。

Runloops是可可的一个可爱的部分,documentation是相当不错的,所以我建议阅读它。

通常,我会避免手动调用runloop。如果您有多个手动调用运行在另一个之上,则会浪费内存并使事情变得非常迅速。

但是,这样做有更好的方法。将你的方法分解成一个-process和一个-didProcess方法。使用-process方法启动异步操作,并在完成时从完成块中调用-didProcess。如果您需要将变量从一种方法传递给另一种方法,则可以将它们作为参数传递给-didProcess方法。

如:

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

您还可以考虑让你的单身自己的调度队列,使其负责处理dispatch_async的东西,因为它会在所有这些讨厌的嵌入式模块保存,如果你总是异步使用它。

如:

[[Singleton sharedInstance] processAyncWithCompletionBlock:^{ 
    NSLog(@"Ready for next step..."); 
    [self didProcess]; 
}]; 
+0

所以,如果我不是要从主队列(主线程)进行回调,我可以使用派遣信号量(http://stackoverflow.com/q/4326350/294661)或甚至使用我的解决方案(无需手动调用runloop)? –

+0

是的......尽管如果你在一个串行队列中,那么提交到该队列的任何块将不会运行,直到块结束。为什么你需要根本阻止线程?你需要从调用方法返回一个值吗? –

+0

我需要等待一个过程才能完成,然后才能继续。不,我不需要返回一个值。 –

1

做你发布的内容很可能会冻结用户界面。不要冻结所有内容,请在完成块中调用您的“下一步”代码。

例子:

dispatch_queue_t queue = dispatch_queue_create("ProcessSerialQueue", 0); 
dispatch_queue_t main = dispatch_get_main_queue(); 

dispatch_async(queue, ^{ 

     Singleton *s = [Singleton sharedInstance]; 

     dispatch_async(dispatch_get_main_queue(), ^{ 
      [s processWithCompletionBlock:^{ 
       // Next step code 
      }]; 
     }); 
}); 
+1

具体来说,派遣块做的工作,并在此块(当工作完成后)内派遣一个块回主队列更新您的UI。这将尽可能有效地使用多线程来保持您的应用程序响应(无沙滩球)。 –

+0

是的,确切地说。 :) –

+0

添加了一个未经测试的示例。 –

-2

不要去创造这样的等待块中值的循环,在块中的变量是只读的,而不是从块内调用您的完成代码。

dispatch_async(queue, ^{ 
    Singleton *s = [Singelton sharedInstance]; 
    [s processWithCompletionBlock:^{ 
     //process is complete 
     dispatch_sync(dispatch_get_main_queue(), ^{ 
      //do something on main queue.... 
      NSLog(@"Ready for next step"); 
     }); 
    }]; 
}); 
NSLog(@"waiting"); 
+1

为什么不呢?如果块中的FYI变量声明为“__block” –

+0

...,则它们也是可写的,如果它们在块中本地声明的话。 –