NSURLSession调用dataTaskWithRequest处理大响应无法触发完成回调问题
NSURLSessionDataTask响应长度说明
NSURLSessionDataTask 没有官方固定的响应长度限制,你遇到的大体积响应回调不触发问题,主要由两个原因导致:
- dataTask的completionHandler机制默认会把所有响应数据全部加载到内存缓冲区,完整接收完成后才会触发回调。如果响应文件体积过大,超过当前App可用内存阈值,系统会中断数据缓冲流程,就会出现抓包已完整收数,但回调始终不触发的现象。
- 你当前代码中
[_dataTask resume]后直接执行sleep(100)会阻塞当前线程,如果你这段代码运行的线程恰好是你传入NSURLSession的代理队列对应的执行线程,就算数据已经接收完成,回调也无法被调度执行,只有cancel调用后才会触发取消对应的回调。
大体积响应的解决方案
首先纠正你的认知误区:downloadTaskWithRequest是支持异步完成回调的,iOS 7及以上系统就提供了带completionHandler的重载,它的工作机制是边接收数据边写入本地临时文件,不会占用大量内存,是大文件下载场景的最优选择。
方案1:使用带回调的downloadTask实现
NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; queue.maxConcurrentOperationCount = 1; NSURLSession* session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:queue]; NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:req completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (error) { if (error.code == NSURLErrorCancelled) { writeLog(LOG_ERROR, "NSURLErrorCancelled"); } return; } // 临时文件会在回调结束后自动删除,需要移动到自定义沙箱路径持久化 NSURL *saveDir = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].firstObject; NSURL *savePath = [saveDir URLByAppendingPathComponent:response.suggestedFilename]; NSError *moveError; [[NSFileManager defaultManager] moveItemAtURL:location toURL:savePath error:&moveError]; if (!moveError) { // 下载完成,可从savePath读取文件 } }]; [downloadTask resume];
方案2:基于dataTask代理实现边收边存
如果必须使用dataTask,可以实现NSURLSessionDataDelegate相关代理方法,每收到一段数据就写入本地文件,不要全量缓存在内存:
- 实现
URLSession:dataTask:didReceiveData:代理,将收到的分片数据追加写入本地文件 - 实现
URLSession:task:didCompleteWithError:代理,处理下载完成或错误逻辑
内容的提问来源于stack exchange,提问作者Zohar81
相关产品推荐
相关产品推荐

