0

I want to execute a few Networking methods using NSOperation for each one of them, and add the the operations to an NSOperationQueue that is set on a back thread, I want the NSOperations to happen serialized.

The call that creates the NSOperation queue is called periodically, each 30 seconds using an NSTimer.

Here is a code snipped so far:

NSTimer *timer;
NSOperationQueue *bgQueue;

- (void)startdDownloadLoop{

    if (![self.timer isValid]){

        self.bgQueue = [[NSOperationQueue alloc] init];
        self.timer = [NSTimer scheduledTimerWithTimeInterval:30.0
                                                      target:self
                                                    selector:@selector(updateDB)
                                                    userInfo:nil
                                                     repeats:YES];

    }

}

- (void)updateDB{

    //        Next code was removed since si similar to the call `[self.bgQueue cancelAllOperations];`, as @robinkunde suggested
    //        for (NSOperation *operation in [self.bgQueue operations]) {
    //            if (operation.isExecuting) {
    //                [operation cancel];
    //            }
    //        }

    [self.bgQueue cancelAllOperations];


    self.bgQueue = [[NSOperationQueue alloc] init];
    self.bgQueue.maxConcurrentOperationCount = 1;

    NSBlockOperation *blockOp1 = [NSBlockOperation blockOperationWithBlock:^{
        [NetworkManager downloadCars];
    }];

    NSBlockOperation *blockOp2 = [NSBlockOperation blockOperationWithBlock:^{
        [NetworkManager downloadPartsForAllCars];
    }];

    NSBlockOperation *blockOp3 = [NSBlockOperation blockOperationWithBlock:^{
        [NetworkManager downloadEachPathSpecs];
    }];

    [self.bgQueue addOperation:blockOp1];
    [self.bgQueue addOperation:blockOp2];
    [self.bgQueue addOperation:blockOp3];

}

The NetworkManager methods are POST/GET methods with completion handlers, that after they finish downloading, they save the date in DB and then update the UI using a notification.

Update:

Since the confusion, I thought to post my actual question regarding the code as fallows:

1- If the execution of the NSOperationQueue will be serial?

2 - if when I am calling the [operation cancel] will stop the execution of the operation?

3 - If when I am calling the cancelAllOperations and the I re-init it is the correct way of clearing the current NSOperationQueue?

Laur Stefan
  • 1,519
  • 5
  • 23
  • 50
  • It probably won't do what you want. Presumably each of the `download...` methods execute asynchronously. If so then as soon as the network operation is dispatched then the block will complete and the next submitted block will begin executing, even though the first download operation is not yet complete. You would probably be better off using a dispatch_group, or even better push notifications rather than polling which is expensive in terms of battery and network traffic – Paulw11 Jun 22 '16 at 12:26
  • As far as cancelling operations, it is up to each block to check the blocks `isCancelled` property and cease work if appropriately. The operation queue can't actually cancel an operation, it just sets the property to indicate to the operation that it should cancel itself. See http://stackoverflow.com/questions/8113268/how-to-cancel-nsblockoperation – Paulw11 Jun 22 '16 at 12:27

2 Answers2

1

1) You can set the property maxConcurrentOperationCount on the queue to 1 to ensure serial execution.

2) This only sets the isCancelled property on the operation. The needs to act on this change and move itself to the finished state so it can be removed from the queue.

As for your specific example: NSBlockOperation is considered finished when all attached blocks have returned. That actually means that unless the calls to NetworkManager are synchronous, the block returns immediately, the operation is considered finished, and the queue starts the next operation. It's entirely possible that the queue finishes before the first network operation completes. If you want to execute them one at a time, this is not the way to do it.

You could use synchronous network operations, but that still wouldn't allow you to cancel them.

Check out this answer: NSURLSession with NSBlockOperation and queues

3) This is related to 2). All cancelAllOperations does is set the isCancelled property on all operations. Until those operation are finished, the queue will continue to exist. Creating a new queue on the same property doesn't change that.

Community
  • 1
  • 1
robinkunde
  • 1,005
  • 9
  • 12
  • Thank you @robinkunde for you prompt response I will change my code accordingly by removing the extra for-loop but, then my question is how can I force stop an NSOperation that is still running? – Laur Stefan Jun 22 '16 at 12:53
0

I see a basic problem in your implementation. The very creation of NSTimer (scheduledTimerWithTimeInterval:) already determines the thread/run-loop on which this Timer will be executed - and it is NOT on your background operation queue, but on the current-thread calling your embedding method - startdDownloadLoop.

I think you should first break this into - initializing your NSTimer without scheduling it, and then schedule it somehow on your backgroundQueue's underlying thread (I am not sure this is possible - but I'm looking for a solution for this for myself... will update here if I find one.

Motti Shneor
  • 2,095
  • 1
  • 18
  • 24