You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何有序串行执行NSURLSessionDataTask并追踪下载进度?

你当前的代码无法实现串行下载的核心原因是:NSURLSessionDataTask 的 resume 方法是异步执行的——你用串行队列只是按顺序调用了启动任务的代码,但每个任务启动后会立刻在NSURLSession的后台队列中并行执行,串行队列无法控制任务的完成顺序。

要实现基于代理回调的串行DataTask下载,核心思路是维护一个待下载任务的队列,在当前任务的didCompleteWithError:代理方法中启动下一个任务。这样既能保证串行执行,又能通过代理方法实时更新下载进度。

具体实现代码

首先在你的类中添加必要属性:

@interface YourViewController () <NSURLSessionDataDelegate>
@property (nonatomic, strong) NSURLSession *session;
@property (nonatomic, strong) NSMutableArray<NSURL *> *downloadURLs; // 待下载URL队列
@property (nonatomic, strong) NSURLSessionDataTask *currentTask; // 当前执行任务
@property (nonatomic, strong) NSMutableData *receivedData; // 当前任务接收的数据
@property (nonatomic, weak) IBOutlet UIProgressView *progressView;
@property (nonatomic, weak) IBOutlet UILabel *statusLabel;
@end

初始化NSURLSession和待下载队列:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 配置Session,指定代理队列为主队列(方便直接更新UI)
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    self.session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    
    // 初始化待下载URL列表
    self.downloadURLs = [NSMutableArray arrayWithObjects:
                         [NSURL URLWithString:@"https://example.com/file.zip"],
                         [NSURL URLWithString:@"https://example.com/file.z01"],
                         [NSURL URLWithString:@"https://example.com/file.z02"],
                         nil];
}

实现启动下载和任务调度的方法:

- (void)download:(id)sender {
    [self testInternetConnection];
    // 启动第一个任务
    [self startNextDownloadTask];
}

// 启动队列中的下一个任务
- (void)startNextDownloadTask {
    if (self.downloadURLs.count == 0) {
        self.statusLabel.text = @"所有文件下载完成";
        self.progressView.progress = 0.0;
        return;
    }
    
    NSURL *nextURL = self.downloadURLs.firstObject;
    [self.downloadURLs removeObjectAtIndex:0];
    
    self.statusLabel.text = [NSString stringWithFormat:@"正在下载: %@", nextURL.lastPathComponent];
    self.receivedData = [NSMutableData data];
    
    // 创建并启动当前任务
    self.currentTask = [self.session dataTaskWithURL:nextURL];
    [self.currentTask resume];
}

实现NSURLSession代理方法,处理进度更新和任务完成:

// 接收到数据时更新进度
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    [self.receivedData appendData:data];
    
    // 计算并更新进度
    NSInteger totalBytes = dataTask.response.expectedContentLength;
    if (totalBytes != NSURLResponseUnknownLength) {
        float progress = (float)self.receivedData.length / totalBytes;
        self.progressView.progress = progress;
    }
}

// 当前任务完成后启动下一个任务
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    if (error) {
        self.statusLabel.text = [NSString stringWithFormat:@"下载失败: %@", error.localizedDescription];
    } else {
        // 将下载完成的数据写入文件
        NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:task.response.URL.lastPathComponent];
        [self.receivedData writeToFile:filePath atomically:YES];
        self.statusLabel.text = [NSString stringWithFormat:@"下载完成: %@", task.response.URL.lastPathComponent];
    }
    
    // 可选:延迟1秒让用户看到完成状态,再启动下一个任务
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self startNextDownloadTask];
    });
}

关键说明

  • 用downloadURLs数组维护待下载任务,每次启动任务时取出第一个URL并移除,严格保证串行顺序
  • 代理队列使用主队列,可直接在代理方法中更新UI(进度条、状态标签)
  • 在didCompleteWithError:中触发下一个任务,确保当前任务完全结束(无论成功或失败)后才开始下一个
  • 若需要处理下载失败的重试逻辑,可在didCompleteWithError:中将失败的URL重新加入队列尾部

内容的提问来源于stack exchange,提问作者Edi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 06:54:22