0

Here is the code

@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

[Edit] The goal is to have a user interaction like this: There is a tableView on screen with 5 or more cells. When ever a user click a cell, background process will perform background process that will take a while. The App will, at 3 second intervals, check to see if the user clicked on another cell. If the user clicked on another cell, I should cancel the current operation from queue, and begin processing the new one the user clicked on.

From the code above i have 2 problems i cant solve.

  1. How do i make it so that my weak reference isnt deallocated in the dispatch_after block? The goal of putting it there is to pause the app for exactly 3 seconds. If dispatch_after is incorrect, then what code do i use there to prevent it becoming nil?

  2. Why is it that my NSOperationQueue become empty after I call dispatch_after? Is there a way to make it not become empty?

Just a coder
  • 15,480
  • 16
  • 85
  • 138
  • Why is it a problem if the weak reference to the operation is deallocated?The weak reference to the operation will be deallocated only if the operation is deallocated — which should only happen when the operation is complete. If the goal of your `dispatch_after` code is just to check and see if the operation should be cancelled, and the operation is nil, there's nothing to cancel. The operation is already done, so you can't cancel it. – jemmons Oct 05 '15 at 17:14
  • I wanted the operation to continue, but only after 3 seconds. I basically want the NSOperationBlock to do this ->> do some work, pause 3 seconds, and then continue doing some more work. But after i did the dispatch_async the operation Block becomes nil.. I was wondering if there was a way to increase the reference count? right before i do dispatch_async.. but im using ARC.. no more ref counts – Just a coder Oct 05 '15 at 19:58
  • 1
    You increase reference counts by creating a strong reference to the thing you want. You should never manipulate reference counts directly. It really sounds like you're trying to fit a square peg into a round hole. If you'd like more help, you're going to have to be more detailed about what you're trying to accomplish. – jemmons Oct 05 '15 at 20:08
  • youre right.. i have now changed my setup. I'll will update my question with the new implementation i went with in sometime. Square peg into a round hole.. true.. – Just a coder Oct 05 '15 at 20:28

2 Answers2

1

dispatch_after schedules the block and returns immediately. So, your NSBlockOperation's executionBlock has almost no work to do — it immediately finishes and is removed from the queue. At that time, the operation is released and so the weak reference becomes nil before the dispatch_after block is called later.

If you do the dispatch_after first and schedule the operation from inside that block, it might suit your needs. You could just use sleep, but I wouldn't recommend that since you will be unnecessarily blocking a thread. See this question for more discussion on NSOperation and delays.

Community
  • 1
  • 1
jtbandes
  • 115,675
  • 35
  • 233
  • 266
  • thanks for your response. You said that if i use `dispatch_after`, that i should schedule the operation from inside that block. I can do this, but before I do the scheduling, how do I check to see if the `aWeakBlockOperation` was cancelled by the user while the 3 second wait was going on? In the code sample above, `aWeakBlockOperation` will become nil, so i cant check.. – Just a coder Oct 03 '15 at 09:37
  • If you need to be able to cancel the initial delay I would recommend a custom NSOperation subclass that runs asynchronously. It can dispatch_after to check its own `cancelled` property. Then it can set `finished` to true. – jtbandes Oct 03 '15 at 09:40
  • Sorry for explaining so badly.. I do not want the delay to be cancelled. The delay should run. But after the delay is run, it should check to see if the user cancelled the operation while it was delaying. So after 3 seconds, then it checks. And if the user has cancelled, then, it wont bother scheduling again. – Just a coder Oct 03 '15 at 09:43
0

You can schedule operation inside the dispatch_after block and declare aBlockOperation as an instance variable/property so aWeakBlockOperation will not became nil.

But you do not need to hassle with the NSBlockOperation to achieve your goal. You can use dispatch_block_t instance variable which you would set to a new value (block with your code you need to be executed after the column is clicked) each time the column is clicked:

@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();
    });
}