2014-02-26 151 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