簡體   English   中英

如何在執行下一條語句之前等待NSURLConnection委托完成?

[英]How can I wait for a NSURLConnection delegate to finish before executing the next statement?

這一直很難搜索。 我發現了一個類似的問題, iOS 5等待代表完成,然后再填充表格嗎? ,但是可接受的答案是“刷新表格視圖”,這對我沒有幫助。 我發現的其他結果往往是在c#中。

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

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

我意識到我可以讓NSURLConnection-recordButtonPressed像我現在,然后繼續委托方法設置代碼connectionDidFinishLoading (或只是封裝設置和方法調用它從那里),但這是犧牲一致的設計,功能性和吮吸。

我是否可以發送給委托人一些簡單的waitUntilDelegateIsFinished:(BOOL)nonAsyncFlag標志,以便進行從網絡提取數據的順序操作?

我意識到我可以像現在一樣在-recordButtonPressed中創建NSURLConnection,然后在委托方法connectionDidFinishLoading中繼續設置代碼(或者只是封裝設置和從那里調用的方法),但這會犧牲功能的一致性設計,吮吸。

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

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

- (void) doPart2 {
}

您可以通過調用來玩各種游戲,以使其更加優雅和通用,但是我的建議是,不要與框架抗爭,因為您所描述的正是異步的本質。 (而且使用在主線程同步的請求,因為該塊的主線程,這是一種禁忌。)

確實,在事件驅動的框架中,“等到”這一概念非常令人厭惡。

為什么不使用同步請求

將您的異步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:的實現可以按以下方式工作:將該塊保存在一個ivar中。 在適當的時候用正確的參數調用它。 但是,使用塊作為隱喻或屬性會增加無意中引入循環引用的風險。

但是,有一種更好的方法:包裝器塊將立即分派到一個掛起的串行分派隊列中,該隊列作為一個ivar保存。 由於隊列已暫停,因此它們將不執行任何塊。 僅在恢復隊列之后,該塊才執行。 您可以在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        
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM