如何使用 dispatch gcd 使用 NSOperationQueue 和 NSBlockOperation?

How to work with NSOperationQueue and NSBlockOperation with dispatch gcd?

这是代码

@interface ViewController ()
@property (nonatomic, strong) NSOperationQueue *queue;
@end


@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];

    _queue = [[NSOperationQueue alloc] init];

    NSBlockOperation *aBlockOperation = [[NSBlockOperation alloc] init];
    __weak NSBlockOperation* aWeakBlockOperation = aBlockOperation;

    [aBlockOperation addExecutionBlock:^{
        NSLog(@"queue should still have the operation. And it does. yay!: %@", [_queue operations]); // This should print correctly. It will show the NSBlock operation correctly residing inside the NSOperationQueue
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"Now queue is empty??: %@", [_queue operations]); // This should print as being empty
            NSLog(@"And a weak block is nil???: %@", aWeakBlockOperation); // This should print out **nil**
            if (![aWeakBlockOperation isCancelled]) {
                // Now i have no way to check the operation
            }

        });
    }];

    [_queue addOperation:aBlockOperation];
@end

[编辑] 目标是进行如下用户交互: 屏幕上有一个包含 5 个或更多单元格的 tableView。当用户单击一个单元格时,后台进程将执行需要一段时间的后台进程。该应用程序将以 3 秒为间隔检查用户是否单击了另一个单元格。如果用户点击了另一个单元格,我应该从队列中取消当前操作,并开始处理用户点击的新操作。

根据上面的代码,我有 2 个问题无法解决。

  1. 如何才能使我的弱引用不在 dispatch_after 块中释放?将它放在那里的目的是使应用程序恰好暂停 3 秒。如果 dispatch_after 不正确,那么我在那里使用什么代码来防止它变成零?

  2. 为什么调用dispatch_after后我的NSOperationQueue变空了?有没有办法让它不变成空的?

dispatch_after 立即安排块和 returns。因此,您的 NSBlockOperation 的 executionBlock 几乎没有工作要做——它立即完成并从队列中移除。那时,操作被释放,所以弱引用在稍后调用 dispatch_after 块之前变为 nil。

如果您先执行 dispatch_after 并从该块内安排操作,它可能适合您的需要。您可以只使用 sleep,但我不建议这样做,因为您会不必要地阻塞线程。有关 NSOperation 和延迟的更多讨论,请参阅 this question

您可以在 dispatch_after 块内安排操作并将 aBlockOperation 声明为实例 variable/property 这样 aWeakBlockOperation 就不会变为 nil。

但您无需费心使用 NSBlockOperation 即可实现您的目标。您可以使用 dispatch_block_t 实例变量,您可以在每次单击该列时将其设置为一个新值(使用您需要在单击该列后执行的代码块):

@implementation ViewController
{
    dispatch_block_t columnBlock;
}

- (void)columnClicked
{
    columnBlock = ^{ ... your code ... };
    __weak dispatch_block_t weakColumnBlock = columnBlock;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            dispatch_block_t colBlock = weakColumnBlock;
            if (colBlock)
                colBlock();
    });
}