NSURLSession downloadTask不释放内存

时间:2014-12-04 17:36:32

标签: objective-c nsurlsession nsurlsessiondownloadtask

由于客户在短时间内无法在他的服务器中实现少量下载,而且当有太多文件(500-1000次下载)时,后台下载非常不一致我决定使用NSURLDownloadTask而不使用后台NSURLSession。 / p>

它适用于大量文件,但有一些不便之处。内存使用率一直在增长,直到我收到内存警告。当我得到它时,我取消了待处理的任务并释放了NSURLCache,但是内存没有被释放,所以当你恢复下载时你得到相同的内存警告。

我没有使用cancelWithResumeData来取消任务。

这是我的代码

- (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];
}

这些是内存使用的两个快照

Memory usage increasing when files are downloading 下载文件时内存使用量增加

Download tasks are stopped but memory is not released 下载任务已停止但内存未释放

为什么我不能释放记忆?

感谢您提供的任何帮助

1 个答案:

答案 0 :(得分:2)

You need to call the invalidateAndCancel method on your NSURLSession instance when you're done using it, otherwise it will leak memory.