【问题标题】:NSURLSession downloadTask not releasing memoryNSURLSession downloadTask 不释放内存
【发布时间】:2014-12-04 17:36:32
【问题描述】:

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

它适用于大量文件,但存在不便。内存使用量一直在增长,直到我收到内存警告。当我得到它时,我取消挂起的任务并释放 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];
}

这是两个内存使用情况的快照

下载文件时内存使用量增加

下载任务停止但内存未释放

为什么我不能释放内存?

感谢您提供的任何帮助

【问题讨论】:

  • 我将从 NSURLSession 文档开始。
  • 我已经阅读了整个文档,但对内存只字未提。只有“重要提示:会话对象保持对委托的强引用,直到您的应用程序显式使会话无效。如果您不使会话无效,您的应用程序会泄漏内存。”我做到了,但没有释放内存

标签: objective-c nsurlsession nsurlsessiondownloadtask


【解决方案1】:

你需要在你的NSURLSession实例上调用invalidateAndCancel方法,当你完成使用它时,它会泄漏内存。

【讨论】:

    猜你喜欢
    • 2017-04-17
    • 1970-01-01
    • 2018-04-15
    • 2017-06-29
    • 2011-06-30
    • 2016-09-14
    • 2015-08-19
    • 2012-05-15
    • 2018-01-26
    相关资源
    最近更新 更多