2013-05-17 52 views
1

這是一個很難搜索。 我發現了一個類似的問題iOS 5 Wait for delegate to finish before populating a table?,但接受的答案是「刷新表格視圖」,這對我沒有幫助。我發現的其他結果往往是在C#中。如何在執行下一條語句之前等待NSURLConnection委託完成?

我有一個從iPhone流到Wowza服務器的應用程序。當用戶點擊記錄時,我生成一個唯一的設備ID,然後將其發送到服務器上的PHP腳本,該腳本返回帶有配置設置(包括rtmp轉儲鏈接)的JSON文檔。

問題是,委託方法是異步的,但我需要在我的- (IBAction)recordButtonPressed方法中的代碼的下一行之前獲取配置設置,因爲該代碼是設置配置文件設置的內容,然後根據這些設置進行記錄。

我已經意識到我可以讓NSURLConnection-recordButtonPressed像我現在,再繼續內設置代碼委託方法connectionDidFinishLoading(或只是封裝設置和方法調用它從那裏),但是這犧牲連貫功能設計,這很糟糕。

是不是有一些簡單的waitUntilDelegateIsFinished:(BOOL)nonAsyncFlag標誌我可以發送給委託人,這樣我就可以從網上獲取數據的順序操作?

+1

你的意思是你想一種同步的方式來進行上傳? – Wain

+0

上傳已經完成。我只想要一種同步的方式在開始錄製之前從php腳本中獲取配置設置。 – user

+1

永遠不要在UI線程中等待。 –

回答

1

我已經意識到我可以使NSURLConnection的在-recordButtonPressed像我現在,然後繼續委託方法connectionDidFinishLoading內設置代碼(或只是封裝設置和方法調用它從那裏),但是這犧牲一致性設計的功能,這很糟糕。

您已經分析並理解了情況,並且已經完美地描述了其可行的解決方案。我只是不同意你的結論。這種事情發生的時間:

- (void) doPart1 { 
    // do something here that will eventually cause part2 to be called 
} 

- (void) doPart2 { 
} 

你描述你可以玩各種遊戲與調用,使這個更優雅和普遍的,但我的建議是,不打框架,正是異步的本質。 (並且不是在主線程上使用同步請求,因爲這會阻塞主線程,這是一個禁止)。

事實上,在事件驅動框架中,「等到」是詛咒。

+0

這*真的*有幫助。那麼你會說iOS中沒有必要顯式等待嗎?還是它有它的目的? – user

+1

我會說,在主線程上顯式等待*從來沒有必要 - 或合法。這是一個事件驅動的框架。你的工作就是準備好接受事件,要求接收事件,安排接收事件 - 但這都是關於接收事件,其順序和時間不受控制。我的書的整個章節致力於試圖讓你思考框架需要你思考的方式:http://www.apeth.com/iOSBook/ch11.html – matt

+1

而在你描述的特定情況下, NSURLConnectionDelegate方法,比如'connectionDidFinishLoading:'*是*你如何「等到」。如果連接完成,您將收到一個事件。你迴應。你不要坐在那裏等你;該框架正在爲您等待後臺線程。 – matt

0

爲什麼不使用synchronous request

+2

切勿在主線程上使用同步請求。如果他在後臺線程中執行它,我們又回到了完全相同的情況 - 他必須能夠重新進入配置的第二部分。 – matt

0

裹在具有完成塊爲參數的輔助方法的異步請求NSURLConnection的:

-(void) asyncDoSomething:(void(^)(id result)completionHandler ;

此方法應在NSURLConnectionDelegate來實現。有關詳細信息,請參閱下面的示例實施和評論

其他地方,在您的操作方法中:

設置完成處理程序。該塊將在主線程上進一步分派,然後執行適當的任何更新表數據,除非結果是錯誤,在這種情況下,您應該顯示警報。

- (IBAction) recordButtonPressed 
{ 
    [someController asyncConnectionRequst:^(id result){ 
     if (![result isKindOfClass:[NSError class]]) { 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       // We are on the main thread! 
       someController.tableData = result; 
      }); 
     }  
    }]; 
} 

方法的實現asyncConnectionRequst:可以工作如下:取塊拿在伊娃。適當時用正確的參數調用它。但是,將塊作爲ivars或屬性會增加無意中引入循環引用的風險。

但是,還有一個更好的辦法:一個包裝塊將立即發送到暫停串行調度隊列 - 這是作爲一個伊娃。由於隊列被暫停,他們不會執行任何塊。只有等待隊列恢復後,該塊纔會執行。您恢復隊列在connectionDidFinish:connectionDidFailWithError:(見下圖):

在你NSURLConnectionDelegate:

-(void) asyncConnectionRequst:(void(^)(id result)completionHandler 
{ 
    // Setup and start the connection: 
    self.connection = ... 
    if (!self.connection) { 
     NSError* error = [[NSError alloc] initWithDomain:@"Me" 
          code:-1234 
         userInfo:@{NSLocalizedDescriptionKey: @"Could not create NSURLConnection"}]; 
      completionHandler(error); 
     }); 
     return; 
    } 
    dispatch_suspend(self.handlerQueue); // a serial dispatch queue, now suspended 
    dispatch_async(self.handlerQueue, ^{ 
     completionHandler(self.result); 
    }); 
    [self.connection start]; 
} 

然後在NSURLConnectionDelegate,派遣一個處理程序,並恢復 處理隊列:

- (void) connectionDidFinishLoading:(NSURLConnection*)connection { 
    self.result = self.responseData; 
    dispatch_resume(self.handlerQueue); 
    dispatch_release(_handlerQueue), _handlerQueue = NULL; 
} 

同樣,當發生錯誤時:

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 
{ 
    self.result = error; 
    dispatch_resume(self.handlerQueue); 
    dispatch_release(_handlerQueue), _handlerQueue = NULL; 
} 

甚至有更好的方法,然而這涉及一些更基本的輔助類,其處理異步架構,其在一天結束的時候讓你的異步代碼看起來像它是同步的:

-(void) doFourTasksInAChainWith:(id)input 
{ 
    // This runs completely asynchronous! 

    self.promise = [self asyncWith:input] 
    .then(^(id result1){return [self auth:result1]);}, nil) 
    .then(^(id result2){return [self fetch:result2];}, nil) 
    .then(^(id result3){return [self parse:result3];}, nil) 
    .then(^(id result){ self.tableView.data = result; return nil;}, ^id(NSError* error){ ... }) 

    // later eventually, self.promise.get should contain the final result   
} 
相關問題