2014-02-26 154 views
1

10我在SOF上关注了使用GCD进行异步调用的几篇文章,如果远程服务器响应速度够快,就像在本地测试服务器上工作和测试时一样正常工作。主线程不等待异步强制

SOF解决方案尝试:

Waiting until two async blocks are executed before starting another block

iPhone - Grand Central Dispatch main thread

现在,我有一个远程服务器建立其采取至少8秒钟返回它的JSON数据和GCD代码充当如果它没有等待异步调用在更新UI之前完成,我最终得到了一个空的表视图。

我能得到这个正确的行为的唯一方法是通过在该行

[NSThread sleepForTimeInterval:10.0]; 

迫使应用程序等待10秒,允许离开“[自我runUnirestRequest:requestUrl]。”返回数据,然后我得到数据。这显然是一个黑客,并希望让我的GCD代码正常工作。

有没有什么办法让UI代码只在异步调用返回数据后才执行?

注意:从runUnirestRequest返回的数据是JSON格式,并被反序列化并放入“salesData”实例中。

我与GCD调用代码如下:

- (void)viewDidLoad 
{ 
...unrelated code... 

[self createActivityIndicator]; 

dispatch_queue_t jsonQueue = dispatch_queue_create("com.ppos.pbsdashboard", NULL); 

// Start block on background queue so the main thread is not frozen 
// which prevents apps UI freeze 
[activityIndicator startAnimating]; 

dispatch_async(jsonQueue, ^{ 

    // Run remote RESTful request 
    [self runUnirestRequest:requestUrl]; 

    // Force main thread to wait a bit to allow ansync dispatch 
    // to get its response with data 
    [NSThread sleepForTimeInterval:10.0]; 

    // Everything in background thread is done. 
    // Call another block on main thread to do UI stuff 
    dispatch_sync(dispatch_get_main_queue(), ^{ 

     // Back within main thread 
     [activityIndicator stopAnimating]; 

     PBSVCDataDisplay *dataVc = [[PBSVCDataDisplay alloc] init]; 

     [dataVc setSalesData:salesData]; 

     [self performSegueWithIdentifier:@"showDataChart" sender:self]; 
    }); 
}); 
} 

runUnirestRequest功能

- (void) runUnirestRequest:(NSString*)urlToSendRequestTo 
{ 
[requestVCMessages setTextAlignment:NSTextAlignmentCenter]; 
[requestVCMessages setText:@"Processing request"]; 

// Handle errors if any occur and display a friendly user message. 
@try{ 

    NSDictionary* headers = @{@"p": settingsPassPhrase}; 

    [[UNIRest get:^(UNISimpleRequest* request) { 
     [request setUrl:urlToSendRequestTo]; 
     [request setHeaders:headers]; 
    }] asJsonAsync:^(UNIHTTPJsonResponse* response, NSError *error) { 

     UNIJsonNode *jsonNde = [response body]; 

     NSDictionary *jsonAsDictionary = jsonNde.JSONObject; 

     salesData = [self deserializeJsonPacket:(NSDictionary*)jsonAsDictionary withCalenderType:[requestParameters calendType]]; 
    }]; 


} 
@catch(NSException *exception){ 
    [requestVCMessages setTextAlignment:NSTextAlignmentLeft]; 
    NSString *errHeader = @"An error has occured.\n\n"; 
    NSString *errName = [exception name]; 
    NSString *errString = nil; 

    // Compare the error name so we can customize the outout error message 
    if([errName isEqualToString:@"NSInvalidArgumentException"]){ 
     errString = [errHeader stringByAppendingString:@"The reason for the error is probably because data for an invalid date has been requested."]; 
    }else{ 
     errString = [errHeader stringByAppendingString:@"General exception. Please check that your server is responding or that you have requested data for a valid date."]; 
    } 
    salesData = nil; 
    [requestVCMessages setText:errString]; 
} 
} 
+0

看看这个问题http://stackoverflow.com/questions/11909629/waiting-until-two-async-blocks-are-executed-before-starting-another-block – tkanzakic

+0

感谢您的回复,我确实尝试过,并且完全一样。除去sleepForTimeInterval调用还会添加错误“嵌套的推动动画可能导致损坏的导航栏”,另外两个涉及“”调用。是否有dispatch_async的onComplete块?不能发现这样的事情。 –

+2

'runUnirestRequest'中有什么?对于'onComplete',你应该使用一个操作队列,而不是GDC队列。 – Wain

回答

0

这不是你如何使用异步调用。你在这里做的是采取异步调用并使其同步。

您的异步代码不应该在乎呼叫是否需要10ms,10秒或10分钟。它应该适用于所有情况。

也许你应该设置它像...

- (void)viewDidLoad 
{ 
    // other stuff... 

    [self runUnirestRequest:requestUrl]; 

    // other stuff... 
} 

- (void)runUnirestRequest:(NSString*)urlToSendRequestTo 
{ 
    // self.activityIndicator should be a property 
    // accessible from anywhere in the class 
    [self.activityIndicator startAnimating]; 

    // don't use try/catch here you already have built in error handling in the call 
    NSDictionary* headers = @{@"p": settingsPassPhrase}; 

    [[UNIRest get:^(UNISimpleRequest* request) { 
     [request setUrl:urlToSendRequestTo]; 
     [request setHeaders:headers]; 
    }] asJsonAsync:^(UNIHTTPJsonResponse* response, NSError *error) { 
     if (error) { 
      // handle the error here not in a try/catch block 
     } 

     UNIJsonNode *jsonNde = [response body]; 

     NSDictionary *jsonAsDictionary = jsonNde.JSONObject; 

     salesData = [self deserializeJsonPacket:(NSDictionary*)jsonAsDictionary withCalenderType:[requestParameters calendType]]; 

     dispatch_async(dispatch_get_main_queue(), ^{ 
      [self.activityIndicator stopAnimating]; 

      PBSVCDataDisplay *dataVc = [[PBSVCDataDisplay alloc] init]; 

      [dataVc setSalesData:salesData]; 

      [self performSegueWithIdentifier:@"showDataChart" sender:self]; 
     }); 
    }]; 
} 

你似乎被包裹在另一个异步块中的JSON请求已经异步调用。

只使用请求已经是异步的事实。

+0

干杯Fogmeister,这个作品完美,很好地解释/指出我出错的地方。非常感谢。 –

0

因为您的runUnirestRequest是aync。你可以在dispatch_async声明中调用它。 [self runUnirestRequest:requestUrl]执行不会等到runUnirestRequest完成。您应该更改runUnirestRequest同步。这可能会解决您的问题。

+0

或者直接删除GCD块并使用异步调用? – Fogmeister

+0

@Fogmeister用'asJson'替换asJsonAsync'可能会修复。 –

+0

我意识到这可能会起作用,但当异步调用已经是异步时,它会增加很多复杂性以使异步调用工作。因此,放弃它周围的额外复杂性,它已经是异步的。 – Fogmeister