簡體   English   中英

NSOperation和NSURLConnection神秘

[英]NSOperation and NSURLConnection mystified

我正在嘗試使用NSOperation和NSOperationQueue從某些服務器下載多個圖像。 我的主要問題是下面的代碼片段和此鏈接http://www.dribin.org/dave/blog/archives/2009/05/05/concurrent_operations/之間的區別是什么? 我更喜歡第二種解決方案,因為我們對操作有更多的控制權,它更清潔,而且如果連接失敗,您可以正確處理它。

如果我嘗試使用下面的代碼從服務器上下載約300張圖像,則我的應用會有很大的延遲,如果我啟動該應用並立即進入主屏幕,然后又回到該應用中,我將崩潰,因為沒有足夠的時間使該應用程序再次激活。 如果我取消注釋[queue setMaxConcurrentOperationCount:1],則用戶界面會響應,並且如果它進入后台並返回到前景,則不會崩潰。

但是,如果我實現與上面的鏈接類似的東西,則無需擔心設置maxConcurrentOperationCount,默認值就可以了。 一切都是響應式的,沒有崩潰,而且似乎所有隊列都更快地完成了。

因此,這引出我的第二個問題,為什么[queue setMaxConcurrentOperationCount:1]在下面的代碼中產生如此大的影響? 從文檔中,我認為將maxConcurrentOperationCount保留為默認值很好,這只是告訴隊列根據某些因素決定最佳值。

這是我關於Stack Overflow的第一篇文章,希望這很有道理,感謝您的幫助!

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//[queue setMaxConcurrentOperationCount:1];

for(NSURL *URL in URLArray) {
    [queue addOperationWithBlock:^{
        NSHTTPURLResponse *response = nil;
        NSError *error = nil;
        NSData * data = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:URL] returningResponse:&response error:&error];

        if(!error && data) {
            [data writeToFile:path atomically:YES];
        }
    }];
}

我將以相反的順序解決這些問題。 你問:

因此,這引出我的第二個問題,為什么[queue setMaxConcurrentOperationCount:1]在下面的代碼中產生如此大的影響? 從文檔中,我認為將maxConcurrentOperationCount保留為默認值很好,這只是告訴隊列根據某些因素決定最佳值。

使用NSURLConnection ,您可以同時下載四個或五個以上的連接。 因此,如果您未設置maxConcurrentOperationCount ,則操作隊列不知道您正在處理NSURLConnection ,因此,當您向隊列中添加300個NSOperation對象時,該隊列將嘗試啟動大量的對象(64-我想)。 但是,由於只能同時運行4或5個NSURLConnection請求,因此由隊列啟動的其余請求將等待,直到4或5個可能的連接之一可用,並且下載請求如此之多,很可能其中許多請求將超時並失敗。

通過將maxConcurrentOperationCount為1,可以對這個問題應用繁重的解決方案,一次只能運行一個。 我建議采取一種折衷方法,即maxConcurrentOperationCount為4,它享有一定程度的並發性(並獲得了巨大的性能提升),但是並沒有那么多,以至於我們冒着連接超時和失敗的風險。

讓我們再回到戴夫Drubin的NSOperation ,他是在你的很大的改進synchronousRequest包裹在一個操作。 話雖如此,他卻忽略了並發請求的一個相當基本的功能,即取消。 您應該進行檢查以查看操作是否已取消,如果已取消,請取消連接:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    if ([self isCancelled]) {
        [connection cancel];
        [self finish];
        return;
    }

    [_data appendData:data];
}

同樣,當他應該在start方法中執行此操作時。

- (void)start
{
    // The Apple docs say "Always check for cancellation before launching the task."

    if ([self isCancelled]) {
        [self willChangeValueForKey:@"isFinished"];
        _isFinished = YES;
        [self didChangeValueForKey:@"isFinished"];
        return;
    }

    if (![NSThread isMainThread])
    {
        [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
        return;
    }

    NSLog(@"opeartion for <%@> started.", _url);

    [self willChangeValueForKey:@"isExecuting"];
    _isExecuting = YES;
    [self didChangeValueForKey:@"isExecuting"];

    NSURLRequest * request = [NSURLRequest requestWithURL:_url];
    _connection = [[NSURLConnection alloc] initWithRequest:request
                                                  delegate:self];
    if (_connection == nil)
        [self finish];
}

我可能會建議對Dave的示例進行其他風格上的改進,但這只是次要的東西,我認為他可以從中獲得大部分的幫助。 未能檢查取消是我身上唯一明顯的大問題。

無論如何,有關並發操作的討論,請參閱《 並發編程指南》中的“ 配置並發執行操作”部分。

另外,在測試如此大的下載量時,我建議您使用Network Link Conditioner(可在Mac / simulator上進行壓力測試),該軟件可在“ Xcode”-“ Open Developer”的“ Hardware IO tools”下找到。工具”-“更多開發人員工具”;如果您啟用iOS設備進行開發,則“設置”應用中“常規”-“開發人員”下還會有一個網絡鏈接調節器設置。 當我們在高度優化的開發環境中測試我們的應用程序時,許多與超時相關的問題並未顯現出來。 使用網絡鏈路調節器來模擬不理想的真實場景非常重要。

我喜歡第二種解決方案,因為我們對操作有更多的控制權

如果您指的是您自己的解決方案,那么實際上是相反的情況:

由於使用了同步便利方法sendSynchronousRequest:您基本上無法實現更多實際要求,例如身份驗證,更好的錯誤處理和自定義數據處理,以及任何非演示或玩具應用程序通常需要的其他功能。

然而,令人遺憾的是缺乏取消操作的能力。 阻止操作一旦啟動就無法取消。 而且也不清楚您要如何取消“ for循環”。 因此,一旦循環開始,您將無法停止。

您可能想在Web和SO上搜索更復雜(和更現代)的方法。

當隊列的MaxConcurrentOperationCount > 1時,很有可能在您仍向隊列中添加更多作業的同時,通過完成操作來鎖定和解鎖隊列,但是當您將其設置為1時,隊列將在隊列中變得更完整之前作業開始工作,這意味着URLArray的循環完成得更快(避免不斷地“死鎖”)。

如果我正確地調用了NSOperationQueue api,則可以“掛起”並“恢復”它。我建議您將其設置為“掛起”,直到完成所有作業的添加,然后將其恢復

暫無
暫無

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

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