首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

iOS NSMutableArray 異步下載崩潰排查:for 循環中 NSURLConnection 併發衝突

     編輯:程式設計綜合問答

在 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 觸發崩潰。

根因分析

  1. 異步非阻塞:connectionWithRequest:delegate: 立即返回,循環繼續,isBusy 保護失效。
  2. 共享可變狀態:downloadData、isBusy 為實例變量,多連接併發回調時數據競爭。
  3. 數組索引失效:若下載過程中數組被修改,索引 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];
        }];
    }];
}

方案二:鏈式回調(傳統 delegate 模式)

在 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 + 併發控制(推薦)

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。
  • 批量下載務必限制併發數(建議 2-4),防止帶寬耗盡或伺服器封禁。
  • 使用 SDWebImage、AFNetworking 等成熟庫處理圖片/文件下載。

理解異步併發本質,選擇合適方案,徹底解決下載崩潰。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved