如何取消AFHTTPRequestOperation并且不保留以前的进度

时间:2013-04-18 19:01:59

标签: ios download afnetworking

我有一个用于测试AFNetworking API的应用。它从服务器下载文档并将它们放在应用程序的沙盒中。用户可以开始下载,暂停/恢复下载并取消下载。按预期启动,暂停和恢复所有工作。但取消会做一些我不希望的事情。

该应用

表格中的每个单元格代表一个“下载”,这是我的模型。表视图控制器侦听单元格中的抽头,并将消息发送到begin / cancel / pause / resume下载。我有一个类DownloadManager来跟踪我的下载模型对象。我有第三课AFFileDownloadAPIClient(使用AFHTTPClient作者推荐的AFNetworking模式。 DownloadManager会在AFFileDownloadAPIClient上调用相应的消息,而NSOperation会在AFHTTPRequestOperation上调用相应的方法。

A simple AFNetworking download tracking app

守则

下面的方法创建一个新的"Content-Disposition",将这些位流传输到一个文件(这很好),并将其抛入队列,为我启动操作。有几点需要注意:1)我在AFFileDownloadAPIClient标题中添加了一些元数据,如内容长度和生成的文件名,因为下载开始时都不知道。请记住这些位正在流式传输给我。 2)AFHTTPRequestOperation保存带有整数索引键的字典,并为UITableView中与索引对应的每个下载保留pause。我发现这有必要稍后检索resumeAFFileDownloadAPIClient等等...

这是- (void)downloadFileWithIndex:(int)index fileName:(NSString *)fileName { // Using standard request operation AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; operation.inputStream = [NSInputStream inputStreamWithURL:request.URL]; operation.outputStream = [NSOutputStream outputStreamToFileAtPath:fileInDocumentsPath append:YES]; // BLOCK level variables // __weak AFHTTPRequestOperation *weakOperation = operation; // For use in download progress BLOCK __weak NSDate *startTime = [NSDate date]; [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { NSHTTPURLResponse *response = (NSHTTPURLResponse*)weakOperation.response; NSString *contentDisposition = [[response allHeaderFields] objectForKey:@"Content-Disposition"]; NSArray *dispositionMetadata = [contentDisposition componentsSeparatedByString:@";"]; NSString *fileName = @"<?>"; // 3rd item is file name if (dispositionMetadata != nil && dispositionMetadata.count == 4) { fileName = [dispositionMetadata objectAtIndex:2]; } if ([_downloadFileRequestDelegate respondsToSelector:@selector(downloadFileRequestFinishedWithData:fileName:atIndex:startTime:)]) [_downloadFileRequestDelegate downloadFileRequestFinishedWithData:responseObject fileName:fileName atIndex:index startTime:startTime]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { if ([_downloadFileRequestDelegate respondsToSelector:@selector(downloadFileRequestFailedWithError:atIndex:startTime:)]) [_downloadFileRequestDelegate downloadFileRequestFailedWithError:error atIndex:index startTime:startTime]; } ]; // Check "Content-Disposition" for content-length [operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) { NSHTTPURLResponse *response = (NSHTTPURLResponse*)weakOperation.response; NSString *contentDisposition = [[response allHeaderFields] objectForKey:@"Content-Disposition"]; NSArray *dispositionMetadata = [contentDisposition componentsSeparatedByString:@";"]; // 4th item is length if (dispositionMetadata != nil && dispositionMetadata.count == 4) { totalBytesExpectedToRead = [[dispositionMetadata objectAtIndex:3] doubleValue]; } // Notify the delegate of the progress if ([_requestProgressDelegate respondsToSelector:@selector(requestDidReceiveBytesForIndex:bytes:totalBytes:)]) [_requestProgressDelegate requestDidReceiveBytesForIndex:index bytes:bytesRead totalBytes:totalBytesExpectedToRead]; }]; // Check to see if operation is already in our dictionary if ([[self.downloadOperations allKeys] containsObject:[NSNumber numberWithInt:index]] == YES) [self.downloadOperations removeObjectForKey:[NSNumber numberWithInt:index]]; // Add operation to storage dictionary [self.downloadOperations setObject:operation forKey:[NSNumber numberWithInt:index]]; // Queue up the download operation. No need to start the operation explicitly [self enqueueHTTPRequestOperation:operation]; }

cancel

现在是pauseresume- (void)cancelDownloadForIndex:(int)index { AFHTTPRequestOperation *operation = [self.downloadOperations objectForKey:[NSNumber numberWithInt:index]]; if (operation != nil) { [operation cancel]; // Remove object from dictionary [self.downloadOperations removeObjectForKey:[NSNumber numberWithInt:index]]; } } - (void)pauseDownloadForIndex:(int)index { AFHTTPRequestOperation *operation = [self.downloadOperations objectForKey:[NSNumber numberWithInt:index]]; if (operation != nil) [operation pause]; } - (void)resumeDownloadForIndex:(int)index { AFHTTPRequestOperation *operation = [self.downloadOperations objectForKey:[NSNumber numberWithInt:index]]; if (operation != nil) [operation resume]; } 方法。请记住,暂停和恢复功能似乎工作正常。

progressView

问题

假设我们想要中途取消下载。我会点击“GO”然后等几秒钟。然后点击“X”取消。下面是前/后图像。 (在左边之前,在右边之后)。

Before/after download and cancel operations

点击“X”后,视图会更改为显示原始“GO”按钮,因此您可以再次尝试,在这种情况下,我将其称为就绪状态(或“之前”)。我不明白的是,当我在刚刚取消的同一个下载中第二次点击“GO”时,我的进度指示器会在原来的1.98 MB处停下来....就好像取消了不删除下载的原始字节,记住它们并继续它停止的地方。为什么呢?

Before/after download and cancel operations

问题

  1. 为什么取消后的下载会在中断的地方继续?
  2. 此行为是预期还是意外?
  3. 我为这篇冗长的帖子道歉,并感谢你阅读这篇文章......

    [编辑1]

    为了更新UITableViewCell中的Download,我至少要做这两件事。

    1. 使用我称之为Download的数据模型类。
    2. 使用管理我的模型- (void)downloadDidReceiveBytesForIndex:(int)downloadIndex bytes:(long long)bytes totalBytes:(double)totalBytes { NSIndexPath *path = [NSIndexPath indexPathForRow:downloadIndex inSection:0]; DownloadTableViewCell *cell = (DownloadTableViewCell*)[self.tableView cellForRowAtIndexPath:path]; Download *download = [_downloadManager.downloads objectAtIndex:path.row]; download.bytesDownloaded += bytes; download.percentageDownloaded = download.bytesDownloaded / totalBytes; // as a factor of 0.0 to 1.0 not 100. cell.downloadProgressView.progress = download.percentageDownloaded; float MB_CONVERSION_FACTOR = 0.000000953674; NSString *bytesText = [NSString stringWithFormat:@"Downloading %.2f of %.2f MB", roundf((download.bytesDownloaded * MB_CONVERSION_FACTOR)*100)/100.0, roundf((totalBytes * MB_CONVERSION_FACTOR)*100)/100.0]; cell.downloadProgressLabel.text = bytesText; } 对象及其状态的数据模型管理器类。
    3. 在表视图控制器中,我在给定索引处侦听给定下载的字节数:

      UITableViewCell

      最后,为了处理滚动表并重用- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; DownloadTableViewCell *cell = (DownloadTableViewCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { UIViewController *temporaryController = [[UIViewController alloc] initWithNibName:@"DownloadTableViewCell" bundle:nil]; // Grab a pointer to the custom cell. cell = (DownloadTableViewCell *)temporaryController.view; [cell initState]; // Listens for method calls on cell cell.delegate = self; cell.selectionStyle = UITableViewCellSelectionStyleNone; } // Set index for this cell (it could be wrong if cell is re-used) cell.downloadIndex = indexPath.row; Download *download = [_downloadManager.downloads objectAtIndex:indexPath.row]; cell.downloading = download.downloading; cell.nameLabel.text = download.name; cell.descriptionLabel.text = download.description; cell.downloadProgressView.progress = download.percentageDownloaded; // Check for completed status cell.completed = download.completed; cell.completedFileNameLabel.text = download.fileName; return cell; } 个对象。我必须确保我的单元格正确创建,对应于正确的下载(在给定的索引处)并反映下载的确切状态。其中一些可能是矫枉过正,但它似乎运作良好。我没有在仪器中测试这个,看看是否/什么时候我泄漏了什么:

      {{1}}

1 个答案:

答案 0 :(得分:1)

在取消下载方法中从集合中删除时,似乎正在释放outputStream。但是,由于下载被取消,因此不会对Download实例进行状态更改。如果该对象继续设置totalBytespercentageDownloaded值,则进度视图将继续反映部分下载的状态。