主线程没有等待异步完成

时间:2014-02-26 12:28:39

标签: ios multithreading asynchronous unirest

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

强制app等待10秒并允许“[self 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];
}
}

2 个答案:

答案 0 :(得分:0)

这不是您使用异步调用的方式。你在这里做的是进行异步调用并使其同步。

您的异步代码不应该关心调用是否需要10毫秒,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请求。

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

答案 1 :(得分:0)

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