NSURLSession downloadTask does not free memory

Due to the fact that the client cannot implement just a few downloads on its server in a short time, and backgroundDownloadTaks were very incompatible when there are so many files (500-1000 downloads), I decided to use NSURLDownloadTask without a background NSURLSession.

It works great with a lot of files, but there are inconveniences. Memory usage always increases until I get a memory warning. When I receive it, I cancel pending tasks and free NSURLCache, but the memory is not released, so when you resume downloading, you get the same memory warning.

I do not use cancelWithResumeData to cancel tasks.

This is my code.

- (void) startDownloadFiles:(NSMutableArray*)arrayFiles { if([[UIDevice currentDevice] isMultitaskingSupported]) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ if (!self.session) { NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; sessionConfiguration.HTTPMaximumConnectionsPerHost = 5; sessionConfiguration.timeoutIntervalForRequest = 0; sessionConfiguration.timeoutIntervalForResource = 0; sessionConfiguration.requestCachePolicy = NSURLCacheStorageNotAllowed; self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil]; } //Resetting session [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { for (NSURLSessionTask *_task in downloadTasks) { [_task cancel]; } [self.session resetWithCompletionHandler:^{ for (id<FFDownloadFileProtocol> file in self.selectedCatalogProducto.downloadInfo.arrayFiles) { if (cancel) break; //Do not create more taks if (![file isDownloaded]) [self startDownloadFile:file]; } }]; }]; }); } } - (void) startDownloadFile:(id<FFDownloadFileProtocol>)file { if (![file isDownloading]) { if ([file taskIdentifier] == -1 && ! cancel) { NSURLSessionDownloadTask *task = [self.session downloadTaskWithURL:[file downloadSource]]; if (task) { [file setDownloadTask:task]; [file setTaskIdentifier:[file downloadTask].taskIdentifier]; [[file downloadTask] resume]; } else { NSLog(@"Error creando tarea para descargar %@", [file downloadSource]); } } } } #pragma mark - Auxiliar Methods -(id<FFDownloadFileProtocol>)getFileDownloadInfoIndexWithTaskIdentifier:(unsigned long)taskIdentifier { for (id<FFDownloadFileProtocol> file in self.selectedCatalogProducto.downloadInfo.arrayFiles) { if (file.taskIdentifier == taskIdentifier) { return file; } } return nil; } #pragma mark - NSURLSessionDownloadTaskDelegate - (void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { if (totalBytesExpectedToWrite == NSURLSessionTransferSizeUnknown) { NSLog(@"Unknown transfer size"); } else { // Locate the FileDownloadInfo object among all based on the taskIdentifier property of the task. id<FFDownloadFileProtocol> file = [self getFileDownloadInfoIndexWithTaskIdentifier:downloadTask.taskIdentifier]; // Calculate the progress. file.downloadProgress = (double)totalBytesWritten / (double)totalBytesExpectedToWrite; // [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // NSLog("%@ ; %f", [file fileName], [file downloadProgress]); // }]; } } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { id<FFDownloadFileProtocol> file = [self getFileDownloadInfoIndexWithTaskIdentifier:downloadTask.taskIdentifier]; if (file) { NSError *error; NSFileManager *fileManager = [NSFileManager defaultManager]; NSURL *destinationURL = [[NSURL fileURLWithPath:tempPath] URLByAppendingPathComponent:[file fileName]]; if ([fileManager fileExistsAtPath:[destinationURL path]]) { NSError *delError = nil; [fileManager removeItemAtURL:destinationURL error:nil]; if (delError) { NSLog(@"Error borrando archivo temporal en %@", [destinationURL path]); } } BOOL success = [fileManager copyItemAtURL:location toURL:destinationURL error:&error]; if (success) { // Change the flag values of the respective FileDownloadInfo object. file.isDownloading = NO; file.isDownloaded = YES; // Set the initial value to the taskIdentifier property of the file object, // so when the start button gets tapped again to start over the file download. } else { NSLog(@"Unable to copy temp file to %@ Error: %@", [destinationURL path], [error localizedDescription]); } if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive) { indexFile++; [[NSOperationQueue mainQueue] addOperationWithBlock:^{ [self numFilesDownloaded:indexFile]; }]; } } } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { id<FFDownloadFileProtocol> file = [self getFileDownloadInfoIndexWithTaskIdentifier:task.taskIdentifier]; if (error != nil && error.code != -999) { //No se ha producido error o se ha cancelado la tarea bajo demanda [[NSOperationQueue mainQueue] addOperationWithBlock:^{ NSLog(@"Download: %@. \n Downlonad completed with error: %@", [task.response.URL absoluteString], [error localizedDescription]); if (!cancel) { NSString *alertBody = @"Se ha producido un error en la descarga, por favor reanúdela manualmente"; if ([error.domain isEqualToString:@"NSPOSIXErrorDomain"] && (error.code == 1) ) { alertBody = @"Se ha interrumpido la descarga debido a que su iPad está bloqueado por código. Por favor reanude la descarga manualmente y evite que el iPad se bloquee"; } // Show a local notification when all downloads are over. UILocalNotification *localNotification = [[UILocalNotification alloc] init]; localNotification.alertBody = alertBody; [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification]; [self errorDownloading:error.localizedDescription]; } }]; } else if (file) { NSLog(@"%@ download finished successfully.", [[file downloadSource] absoluteString]); file.taskIdentifier = -1; // In case there is any resume data stored in the file object, just make it nil. file.taskResumeData = nil; file.downloadTask = nil; } else if (cancel) { NSLog(@"Tarea cancelada"); } if (self.selectedCatalogProducto.downloadInfo.arrayFiles.count == indexFile && !cancel) { [[NSOperationQueue mainQueue] addOperationWithBlock:^{ if (!complete) { complete = YES; [self downloadComplete]; } }]; } task = nil; } #pragma mark - Memory warning - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; if (_isDownloading) { [self storeCatalogProductInfo:self.selectedCatalogProducto andDownloadInfo:YES]; [self stopDownloading]; } [[NSURLCache sharedURLCache] removeAllCachedResponses]; [self.session.configuration.URLCache removeAllCachedResponses]; } 

And these are two snapshots of memory usage

Memory usage increasing when files are downloading Memory usage increases when downloading files

Download tasks are stopped but memory is not released Task loading stopped but memory not released

Why can't I free my memory?

Thanks for the help provided.

+6
source share
1 answer

You need to call the invalidateAndCancel method on your NSURLSession instance when you finish using it, otherwise it will result in a memory leak.

+1
source

Source: https://habr.com/ru/post/979132/


All Articles