【问题标题】:How do I cancel an NSOperation in my object when my UITableViewCell is no longer visible?当我的 UITableViewCell 不再可见时,如何取消对象中的 NSOperation?
【发布时间】:2013-01-20 22:51:11
【问题描述】:

是我的- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 代表我有以下代码:

if ([movie isDownloaded])
    cell.detailTextLabel.text = movie.duration;
else
{
    cell.detailTextLabel.text = @"";

    [movie downloadInQueue:self.downloadQueue completion:^(BOOL success) {

        UITableViewCell *updateCell = [tblView cellForRowAtIndexPath:indexPath];            
        if (updateCell)
        {
            updateCell.detailTextLabel.text = movie.duration;
            [updateCell setNeedsLayout];
        }
    }];
}

调用 Movie.m 并运行此代码:

- (void)downloadInQueue:(NSOperationQueue *)queue completion:(void (^)(BOOL success))completion
{
    if (!self.isDownloading)
    {
        self.downloading = YES;

        [queue addOperationWithBlock:^{
            BOOL success = NO;

            AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:self.fileURL];
            CMTime timeduration = playerItem.duration;
            float seconds = CMTimeGetSeconds(timeduration);
            self.duration = [self timeFormatted:seconds];

            self.downloading = NO;
            self.downloaded = YES;
            success = YES;

            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                completion(success);
            }];
        }];
    }
}

当我的单元格变得可见时,如果 Movie 对象中的 NSOperation 尚未运行,我想取消它(将其从队列中删除)。我知道我可以继承 UITableViewCell 并做这样的事情:

- (void)willMoveToWindow:(UIWindow *)newWindow
{
    [super willMoveToWindow:newWindow];

    if (newWindow==nil) {
            // Cell is no longer in window so cancel from queue
    }
}

问题...如何在 UITableViewCell 委托呼叫中取消我的 Movie NSOperation?有代表或某种NSNotification?我需要知道单元格的 indexPath 才能从我的数组中获取正确的 Movie 对象并取消操作。

【问题讨论】:

    标签: ios multithreading cocoa-touch uitableview nsoperation


    【解决方案1】:

    从 iOS 6 开始,您可以使用UITableView Delegate protocol 的 tableView:didEndDisplayingCell:forRowAtIndexPath: 方法。当单元格从表格视图中移除时会调用它(当它不再可见时会发生这种情况)。

    - (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
        // Cancel operation here for cell at indexPath
    }
    

    【讨论】:

      【解决方案2】:

      顺便说一句,我刚刚在这里看到了这个问题,但已经回答了here。但我同意 Nebs(他的回答应该被接受)。

      正如 Nebs 所说,在 iOS 6 中,使用 didEndDisplayingCell。因此,它可能看起来像:

      - (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
      {
          Movie *movie = self.movies[indexPath.row];
      
          if ([movie isDownloading])
              [movie cancelDownload];
      }
      

      但在早期版本中,您必须执行诸如回复scrollViewDidScroll,手动查看indexPath 中不再包含哪些indexPathsForVisibleRows 对象,并从那里取消操作

      为了取消操作,您需要更改此downloadInQueue,以便它不只是调用addOperationWithBlock,而是应该创建一个NSBlockOperation 并将其添加到队列中,但还要保存一个weak 引用这样你就可以像这样编写cancelDownload 方法:

      @interface Movie ()
      
      @property (nonatomic, getter = isDownloaded) BOOL downloaded;
      @property (nonatomic, getter = isDownloading) BOOL downloading;
      @property (nonatomic, weak) NSOperation *operation;
      
      @end
      
      @implementation Movie
      
      - (void)downloadInQueue:(NSOperationQueue *)queue completion:(void (^)(BOOL success))completion
      {
          if (!self.isDownloading)
          {
              self.downloading = YES;
      
              NSOperation *currentOperation = [NSBlockOperation blockOperationWithBlock:^{
                  BOOL success = NO;
      
                  self.playerItem = [AVPlayerItem playerItemWithURL:self.webURL];
                  if (self.playerItem)
                  {
                      success = YES;
                      CMTime timeduration = self.playerItem.duration;
                      float seconds = CMTimeGetSeconds(timeduration);
                      self.durationText = [NSString stringWithFormat:@"%f", seconds];
                  }
                  self.downloading = NO;
                  self.downloaded = YES;
      
                  [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                      completion(success);
                  }];
              }];
      
              [queue addOperation:currentOperation];
              self.operation = currentOperation;
          }
      }
      
      - (void)cancelDownload
      {
          if ([self isDownloading] && self.operation)
          {
              self.downloading = NO;
              [self.operation cancel];
          }
      }
      
      @end
      

      【讨论】:

        【解决方案3】:

        看起来您应该将单元格或索引路径传递给downloadInQueue:completion:。然后,您可以使用字典、数组或关联对象来维护单元格及其操作之间的连接。

        【讨论】:

        • ...或者您可以使用框架提供的功能,正如 Nebs 指出的那样。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多