[英]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.