当弹出 ViewController 时停止执行 GCD

Stop execution of a GCD, when the ViewController is popped

我的应用程序中有一个 -[tableView reloadData] 方法,为了加快执行速度,我在 GCD 中按以下方法调用了它。

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{

    [tableView reloadData];
});    

但是当我弹出 viewController 时,应用程序崩溃并显示此消息 [SecondViewController numberOfSectionsInTableView:]: message sent to deallocated instance 0x7fe376206e10。我假设 [reloadData] 在我退出 ViewController 后仍在执行。我怎样才能停止它的执行?我应该把它变成一个 NSOperation 吗?如果可以,我该怎么做?

您的代码有几个问题。以下是导致崩溃的一系列事件

1) 块捕获 tableView 并使其保持活动状态。

2) 然后你的视图控制器被 pop 释放,

3) 该块执行并且 tableView 调用它的数据源(您的视图控制器),该数据源现在已被释放。

您可以通过停止上面的 #1 或 #3 来解决这个问题。我建议#1。 (我在这里假设 ARC)

__weak UITableView *weakTableView = tableView;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
    [weakTableView reloadData];
});

如果这不起作用,则可能是其他原因使 tableView 保持活动状态。您应该调查到底是什么在做,但您也可以通过阻止视图控制器中的 #3 dealloc 方法来修复崩溃:

- (void)dealloc {
    self.tableView.dataSource = nil;
    self.tableView.delegate = nil;
}

不幸的是,您无法停止 GCD 的执行,但还有另一种方法可以修复此错误。由于此线程中的主要问题是关于停止执行,我将 post 根据您的要求使用 NSOperation 解决方案。

1- 创建一个 NSOperationQueue

NSOperationQueue *_myQueue;
_myQueue = [NSOperationQueue new];
_myQueue.name = @"com.my.queue";

2- 从队列中重新加载 table。 (我会用积木好吗?)

[_myQueue addOperationWithBlock:^{

    //your really expensive function
    //and your table reload call
    [tableView reloadData];
}];

3- 现在您可以使用

取消执行
//maybe you will want to do this on viewDidDisappear
[_myQueue cancelAllOperations];

更新:

糟糕,我看到你在延迟 table 重新加载调用,但 NSOperation 没有延迟机制。要解决此问题,您可以使用

模拟延迟
[NSThread sleepForTimeInterval:1.5];

addOperationWithBlock: 内调用 [tableView reloadData]; 之前或像现在一样继续使用 GCD,并更改 tableView 对 weak 的引用以避免块保留你的 tableView 对象,像这样:

__weak __typeof__(tableView) weakTable = tableView;

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{

    //Now, if your table object was released at this point, the reloadData
    //will be ignored
    [weakTable reloadData];
});

希望对您有所帮助...

作为一个选项,您可以弱保留数据源并检查它:

__weak __typeof__(self) dataSource = self; // or whatever it is

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
  if (dataSource!=nil)    
  {
    [weakTable reloadData];
  }
});

仍然不太可能发生崩溃,如果您持有数据源,请将新的数据源设置到 table 视图并再次删除该数据源,因此它会被释放。