【问题标题】:How to work with NSOperationQueue and NSBlockOperation with dispatch gcd?如何使用调度 gcd 使用 NSOperationQueue 和 NSBlockOperation?
【发布时间】:2015-10-03 09:05:40
【问题描述】:

这里是代码

@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 不正确,那么我在那里使用什么代码来防止它变为 nil?

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

【问题讨论】:

  • 如果操作的弱引用被解除分配为什么会出现问题?操作的弱引用只有在操作被解除分配时才会被解除分配——这应该只在操作完成时发生。如果您的dispatch_after 代码的目标只是检查操作是否应该被取消,并且操作为零,则没有什么可以取消的。操作已经完成,无法取消。
  • 我希望操作继续,但仅在 3 秒后。我基本上希望 NSOperationBlock 这样做 ->> 做一些工作,暂停 3 秒,然后继续做一些更多的工作。但是在我执行 dispatch_async 之后,操作 Block 变为 nil .. 我想知道是否有办法增加引用计数?就在我执行 dispatch_async 之前.. 但我正在使用 ARC.. 没有更多的引用计数
  • 通过创建对所需内容的强引用来增加引用计数。你不应该直接操纵引用计数。听起来你真的想把一个方形钉子装进一个圆孔里。如果您需要更多帮助,则必须更详细地说明您要完成的工作。
  • 你说得对。我现在已经改变了我的设置。我会用我在某个时候使用的新实现来更新我的问题。方钉插入圆孔.. 对..

标签: ios cocoa grand-central-dispatch nsoperation


【解决方案1】:

dispatch_after 调度块并立即返回。因此,您的NSBlockOperation 的 executionBlock 几乎没有任何工作要做——它立即完成并从队列中删除。那时,操作被释放,因此在稍后调用 dispatch_after 块之前,弱引用变为 nil。

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

【讨论】:

  • 感谢您的回复。你说如果我使用dispatch_after,我应该从该块内安排操作。我可以这样做,但是在进行调度之前,如何检查 aWeakBlockOperation 在等待 3 秒时是否被用户取消?在上面的代码示例中,aWeakBlockOperation 将变为 nil,所以我无法检查..
  • 如果您需要能够取消初始延迟,我会推荐一个异步运行的自定义 NSOperation 子类。它可以 dispatch_after 检查自己的cancelled 属性。然后它可以将finished 设置为true。
  • 抱歉解释得这么糟糕。我不希望延迟被取消。延迟应该运行。但是在延迟运行之后,它应该检查用户是否在延迟时取消了操作。所以在 3 秒后,它会检查。如果用户已经取消了,那么它就不会再打扰调度了。
【解决方案2】:

您可以在 dispatch_after 块内调度操作并将 aBlockOperation 声明为实例变量/属性,这样 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();
    });
}

【讨论】:

    猜你喜欢
    • 2014-10-23
    • 1970-01-01
    • 1970-01-01
    • 2011-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-04
    相关资源
    最近更新 更多