在 NSMutableArray 遍歷發起多個 NSURLConnection 異步下載時,若無併發控制,極易導致數組越界、數據竄改或崩潰。本文分析典型崩潰場景並給出三種解決方案。
- (void)complexDownload {
for (int i = 0; i < downloadArray.count; i++) {
if (isBusy == NO) {
// 創建連接...
NSURLConnection *conn = [NSURLConnection connectionWithRequest:req delegate:self];
if (conn) {
self.downloadData = [NSMutableData data];
isBusy = YES;
}
}
}
}
問題:for 循環同步跑完,isBusy 尚未在回調中重置為 NO,導致**只發起第一個請求**,或因併發訪問 downloadArray/downloadData 觸發崩潰。
connectionWithRequest:delegate: 立即返回,循環繼續,isBusy 保護失效。downloadData、isBusy 為實例變量,多連接併發回調時數據競爭。i 可能越界。用 NSOperationQueue 限制最大併發數為 1:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 1;
for (NSURL *url in downloadArray) {
[queue addOperationWithBlock:^{
// 同步下載或創建連接,設置 delegate 為獨立對象
NSData *data = [NSData dataWithContentsOfURL:url]; // 簡化示例
// 主線程回調更新 UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.textView.text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}];
}];
}
在 connectionDidFinishLoading: 中遞歸啟動下一個:
- (void)startDownloadAtIndex:(NSUInteger)index {
if (index >= downloadArray.count) return;
NSURL *url = downloadArray[index];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
[NSURLConnection connectionWithRequest:req delegate:self];
currentIndex = index; // 記錄當前索引
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// 處理數據...
[self startDownloadAtIndex:currentIndex + 1]; // 啟動下一個
}
NSURLSession *session = [NSURLSession sharedSession];
for (NSURL *url in downloadArray) {
[[session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *resp, NSError *err) {
if (!err) {
dispatch_async(dispatch_get_main_queue(), ^{
self.textView.text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
});
}
}] resume];
}
// 若需限制併發,可用 NSOperationQueue 包裝 dataTask
NSURLConnection(iOS 9+ 棄用),遷移至 NSURLSession。NSMutableData。SDWebImage、AFNetworking 等成熟庫處理圖片/文件下載。理解異步併發本質,選擇合適方案,徹底解決下載崩潰。