带有完成处理程序的异步调用上的多个URLSession dataTask导致内存增加

时间:2019-02-06 09:25:26

标签: swift nsurlsession completionhandler nsurlsessiondatatask

我正在迅速开发一个上传项目。我正在使用imagepickercontroller拍摄非常大的文件(视频,图片大小超过500 MB),并将此文件分成大小为1 MB的块。然后,我将这些块发送到远程服务器,并使其在服务器中进行碎片整理,然后向用户显示此文件。

如果文件大小小于300 MB,我没有问题。但是在达到此大小后,内存会增加太多,并且应用程序将崩溃。实际上,在每种情况下,内存使用量都在增加,但是没有崩溃。

当我在控制台上查看进度时,我看到URLSession任务开始。但是,由于这些任务正在等待来自完成处理程序的响应,因此任务队列在增加,内存使用量也在增加。有没有办法在任务开始时,该任务的完成处理程序也开始?我认为如果可以同时释放任务队列,我的问题就可以解决。我在等你的帮助。

let url:URL = URL(string: "\(addressPrefix)UploadFile")!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
request.httpMethod = "POST"

let bodyData = "\(metaDataID)~\(chunkIndex)~\(chunkSize)~\(chunkHash)~\(wholeTicket)~\(fileDataString)"

request.httpBody = bodyData.data(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue));
request.timeoutInterval = .infinity

let task = session.dataTask(with: request as URLRequest, completionHandler: {(data, response, error) in
    guard let _:Data = data, let _:URLResponse = response, error == nil else {
       var attemptCounter = 1
       if attemptCounter <= 3 {
            completion("\(attemptCounter).attempt",chunkSize, error)
            attemptCounter += 1
        }
         return
     }
    let jsonStr = String(data: data!, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue))
    completion(jsonStr, chunkSize, error) 
 SingletonConnectionManager.sharedConnectionDataManager.dataTasks["uploadFile"] = nil 
})  
SingletonConnectionManager.sharedConnectionDataManager.dataTasks["uploadFile"] = task 
task.resume()

---我在表视图控制器中从此函数调用此URLSession任务

 tmpConnection.uploadFile(chunk, metaDataID!, chunkIndex: chunkIndex, completion: {(result, chunkSize, error) in
   // I want to enter immediately when 'uploadFile' get called })

1 个答案:

答案 0 :(得分:0)

直到所有请求都发送完毕,请求才真正等待。当一切运行正常时,每个回调都会在关联请求完成时发生,并且早点实现是没有意义的,因为回调提供了服务器的响应(您可能无法在请求之后返回该响应)已经完全发送出去了。

这里的问题是,由于同时启动太多任务,您完全阻塞了会话。当您一次在单个会话中创建大量任务时,NSURLSession中存在一个已知的错误,该错误会导致它崩溃。当您在IIRC会话中收到太多任务时,该会话将完全停止调用回调,并且基本上该会话变得无法使用。 (还有另一个堆栈溢出问题,几年前已经讨论过了,尽管我现在似乎找不到它。)

由于任务永远不会完成,因此您的应用程序最终会泄漏用于主体数据的所有内存,这意味着您的应用程序会分配越来越多的内存,直到被逐出为止。

解决此问题的唯一方法是停止一次将所有请求都添加到会话中。首先开始几个任务(最多八个部分),然后等待发送下一个部分,直到前面的一个部分完成或失败。这种方法不仅可以防止您阻塞NSURLSession,而且可以防止分配大量的内存来容纳所有请求主体NSData对象,这些对象当前都一次位于RAM中。

我建议保留代表每个未发送块的NSNumber对象的NSMutableArray。这样,您就知道还有什么要发送,您可以循环到8并提取前8个数字,然后发送带有这些数字的数据块。请求成功完成后,从数组中获取下一个数字,然后使用该数字发送代码块。

此外,您不应该在重试特定次数后停止。相反,当请求失败时,请检查失败以决定是重试(网络故障)还是放弃(服务器错误)。然后,使用可达性来等待,直到合适的时机再试一次,然后在提示目标主机可达时再试一次。仅当用户通过单击“取消”按钮或类似按钮明确要求您取消上传时,才取消上传。如果用户要求您取消上传,请关闭您的数据结构,以免您不发起任何新请求,然后使URL会话无效。

相关问题