大中央调度和function

我一直在看这个问题 ,试图解决我在这里的问题。 tl; dr是我想使用GCD让我在执行一些任务时显示“等待”屏幕,然后在完成时隐藏屏幕。 现在,我有

- (void) doStuff { // Show wait on start [self.waitScreen setHidden:NO]; dispatch_queue_t queue = dispatch_queue_create("com.myDomain.myApp",null); dispatch_async(queue, ^{ dispatch_async(dispatch_get_main_queue(), ^{ // Double nesting the dispatches seems to allow me to do UI changes as part of 'Code to execute' below. // If I do not double nest like this, the UI still freezes while it executes dispatch_queue_t queue2 = dispatch_queue_create("com.myDomain.myApp",null); dispatch_async(queue2, ^{ dispatch_async(dispatch_get_main_queue(), ^{ // Code to execute { //... Do my time consuming stuff here ... // For testing purposes, I'm using int i = 0; while (i < 1000000000) { i++; } } // Hide Wait Screen on End [self.waitScreen setHidden:YES]; }); }); }); }); } 

而这只是我想要的。 我像这样打电话给[self doStuff]

 - (IBAction) buttonTouchUpInside:(id)sender { [self doStuff]; } - (void) doStuff { // ... code from first code block here ... } 

这一切都完美的作品。 现在,我发现我将需要在函数调用中使用它。 所以我需要像这样的东西:

 - (IBAction) buttonTouchUpInside:(id)sender { NSMutableString *string= [self doStuff]; // ... use 'string' to do other stuff ... // For testing, I'm using self.label.text = string; } - (NSMutableString *) doStuff { // ... code from first code block here ... } 

我如何需要改变语法,以便能够通过dispatch_async传递variables?


我看着苹果文件,并试图

 - (IBAction) buttonTouchUpInside:(id)sender { NSMutableString *string= [self doStuff]; // ... use 'string' to do other stuff - shows 'nil' when I put breakpoints here ... // For testing, I'm using self.label.text = string; } - (NSMutableString *) doStuff { __block NSMutableString *string = [[NSMutableString alloc] initWithString:@"Initing"]; // Show wait on start [self.waitScreen setHidden:NO]; dispatch_queue_t queue = dispatch_queue_create("com.myDomain.myApp",null); dispatch_async(queue, ^{ dispatch_async(dispatch_get_main_queue(), ^{ dispatch_queue_t queue = dispatch_queue_create("com.myDomain.myApp",null); dispatch_async(queue, ^{ dispatch_async(dispatch_get_main_queue(), ^{ // Code to execute { int i = 0; while (i < 1000000000) { i++; } [string setString:@"Hello World"]; } // Hide Wait Screen on End [self.waitScreen setHidden:YES]; }); }); }); }); return string; } 

但是当我运行这个时, label只显示Initing 。 我需要它来显示Hello World 。 我所做的任何改变都没有通过。

看过其他一些问题后,这似乎被称为“竞赛条件”。 据我了解,一旦它碰到dispatch_async ,块中的代码开始在新线程上运行,但块外的代码继续在旧线程上同时运行。 所以它看起来像线程外的线程得到self.label.text = string之前,运行该块的线程可以到达[string setString:@"Hello World"]; 。 如何使self.label.text = string行等到[string setString:@"Hello World"]; 完成?

首先,你的双重嵌套的理由是有缺陷的。 不知道为什么它可能工作,但正确的方法是做一些asynchronous工作,任何时候你想更新ui包装在主队列中的代码块。

 - (void) doStuff { // Show wait on start [self.waitScreen setHidden:NO]; // queue should be a global variable, you don't want to create it every time you // execute doStuff dispatch_async(queue, ^{ // Code to execute { //... Do my time consuming stuff here ... // For testing purposes, I'm using int i = 0; while (i < 1000000000) { i++; } } dispatch_async(dispatch_get_main_queue(), ^{ // Hide Wait Screen on End [self.waitScreen setHidden:YES]; }); }); } 

由于你的队列asynchronous执行工作,你不能简单地从doStuff返回一个值而不等待,这会阻塞你再次调用doStuff的队列。

如果您只想在标签上设置一个值,那么在主队列上执行的块中也要这样做,就像隐藏等待屏幕一样。

另一种常见的方式是,在工作完成后尽快提供一个callback块来执行。

 - (void) doStuffWithCompletionBlock:(void(^)(NSString *))block { // again, a global variable for the queue dispatch_async(queue, ^{ // do some work here that shouldn't block the UI dispatch_async(dispatch_get_main_queue(), ^{ block(@"My result string"); }); }); } - (void) myAction:(id)sender { __weak typeof(self) weakSelf = self; [self doStuffWithCompletionBlock:^(NSString *result) { weakSelf.label.text = result; }]; } 

注意,我在主队列上调用完成块,这是一个select。 你可以把它留在外面,但是在稍后的完成块本身,你仍然可以在主队列上做所有的UI更新。