4

Hi I am building an app using Swift. I need to process notifications in a specific order. Therefore I am trying to use addOperations waitUntilFinished.

Here is what I did:

let oldify = NSOperation()
    oldify.completionBlock = {
println("oldify")
}
let appendify = NSOperation()
    appendify.completionBlock = {
println("appendify")
}
let nettoyify = NSOperation()
    nettoyify.completionBlock = {
println("nettoyify")
}
NSOperationQueue.mainQueue().maxConcurrentOperationCount = 1
NSOperationQueue.mainQueue().addOperations([oldify, appendify, nettoyify], waitUntilFinished: true)

With this code none of the operations is being executed. When I try this instead:

NSOperationQueue.mainQueue().maxConcurrentOperationCount = 1
NSOperationQueue.mainQueue().addOperation(oldify)
NSOperationQueue.mainQueue().addOperation(appendify)
NSOperationQueue.mainQueue().addOperation(nettoyify)

The operations get executed but not in the right order.

Does anyone know what I'm doing wrong? I am getting confident in swift but completely new to NSOperations

Quentin Malgaud
  • 405
  • 6
  • 21
  • possible duplicate of [NSOperationQueue serial FIFO queue](http://stackoverflow.com/questions/10948804/nsoperationqueue-serial-fifo-queue) – Azat May 01 '15 at 18:57

1 Answers1

6

A couple of issues:

  1. You are examining behavior of the completion block handlers. As the completionBlock documentation says:

    The exact execution context for your completion block is not guaranteed but is typically a secondary thread. Therefore, you should not use this block to do any work that requires a very specific execution context.

    The queue will manage the operations themselves, but not their completion blocks (short of making sure that the the operation finishes before its completionBlock is started). So, bottom line, do not make any assumptions about (a) when completion blocks are run, (b) the relation of one operation's completionBlock to other operations or their completionBlock blocks, etc., nor (c) which thread they are performed on.

  2. Operations are generally executed in the order in which they were added to the queue. If you add an array of operations, though, the documentation makes no formal assurances that they are enqueued in the order they appear in that array. You might, therefore, want to add the operations one at a time.

  3. Having said that, the documentation goes on to warn us:

    An operation queue executes its queued operation objects based on their priority and readiness. If all of the queued operation objects have the same priority and are ready to execute when they are put in the queue—that is, their isReady method returns YES—they are executed in the order in which they were submitted to the queue. However, you should never rely on queue semantics to ensure a specific execution order of operation objects. Changes in the readiness of an operation can change the resulting execution order. If you need operations to execute in a specific order, use operation-level dependencies as defined by the NSOperation class.

    To establish explicit dependencies, you might do something like:

    let oldify = NSBlockOperation() {
        NSLog("oldify")
    }
    oldify.completionBlock = {
        NSLog("oldify completion")
    }
    
    let appendify = NSBlockOperation() {
        NSLog("appendify")
    }
    appendify.completionBlock = {
        NSLog("appendify completion")
    }
    
    appendify.addDependency(oldify)
    
    let nettoyify = NSBlockOperation() {
        NSLog("nettoyify")
    }
    nettoyify.completionBlock = {
        NSLog("nettoyify completion")
    }
    
    nettoyify.addDependency(appendify)
    
    let queue = NSOperationQueue()
    queue.addOperations([oldify, appendify, nettoyify], waitUntilFinished: false)
    
  4. BTW, as you'll see above, you should not add operations to the main queue in conjunction with the waitUntilFinished. Feel free to add them to a different queue, but don't dispatch from a serial queue, back to itself, with the waitUntilFinished option.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Thank you very much @Rob. It looks much more clear to me now. I am still facing a problem though. In the appendify block I am running a request on parse using `findObjectsInBackgroundWithBlock`. The result is that oldify, appendify and nettoyify are being executed in the right order but the request (supposedly inside appendify) returns results after everything. I am trying to perform that request on the main thread as I already set `queue.qualityOfService = NSQualityOfService.UserInitiated` – Quentin Malgaud May 01 '15 at 21:35
  • Your operations are doing `findObjectsInBackgroundWithBlock`? If so, that's an asynchronous request so I bet your operation is likely completing before the Parse query is done. Two solutions: Either wrap these in asynchronous `NSOperation` subclass that only manually completes the operation when the request is done or have your operations issue synchronous requests instead (but make sure to add these operations to your own queue, not the main queue). – Rob May 01 '15 at 22:19
  • Thank you so much @Rob. I choose to do synchronous requests using `.find()` instead of `findObjectsInBackgroundWithBlock`. I had to set `waitUntilFinished` to true though. Is that a problem since all of this does not happen on the main queue? – Quentin Malgaud May 04 '15 at 20:01
  • Why would you need `waitUntilFinished`? Put anything dependent upon theses `find`-based operations in another operation and make that dependent upon those other operations. You really don't want to use `waitUntilFinished` unless absolutely necessary (which it isn't here). In answer to your question, no, you'd never call `waitUntilFinished` from the main thread (regardless of what the operations are doing on the background thread). – Rob May 04 '15 at 20:51