5

I have a data loading operation that needs to be run off the main thread to avoid potential blocking issues. To do this, I use an NSOperationQueue and NSOperations.

One issue that has come up, however, is that one of the operations exists to spawn additional operations based on incoming information. Trying to solve this also solved some minor issues I had elsewhere, as the solution I hit upon was to give the NSOperation it's own queue for sub-tasks.

The problem is that as soon as 'main' exits, the NSOperation is going to be marked as 'finished', regardless of whether or not the sub-queue is finished processing; how do I override that behavior?

icodebuster
  • 8,890
  • 7
  • 62
  • 65
RonLugge
  • 5,086
  • 5
  • 33
  • 61
  • Sounds like you're looking for a [concurrent `NSOperation`](http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSOperation_class/Reference/Reference.html)? – Carl Veazey May 10 '13 at 03:00
  • @CarlVeazey Not... exactly, but it looks like the steps used to create a concurrent NSOperation do the same thing I'm actually looking for. You have to override how the start method handles isFinished/isExecuting, and I think that's exactly what I need to do here. I need to override start so it doesn't set isFinished the instant main ends. Which means handling the KVO stuff when I change those properties, oh joy. – RonLugge May 10 '13 at 03:11
  • @CodaFi I'm not sure if I made myself clear, but the answer you've suggested doesn't seem to make sense. Dependencies prevent an operation from executing, they don't prevent it from finishing once it's begun executing. At least, that's what the references appear to be saying. – RonLugge May 10 '13 at 03:53
  • Ah, my bad. Why doesn't [setSuspended:](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSOperationQueue_class/Reference/Reference.html#//apple_ref/occ/instm/NSOperationQueue/setSuspended:) work for you then? There's no point in having a "sub-queue" when dealing with stack-esque objects like NSOperationQueues. Two queues with different concurrency specs makes more sense – CodaFi May 10 '13 at 03:55
  • @CodaFi because suspending the 'parent' queue doesn't keep the NSOperation from executing (and suspending the NSOperation's queue would prevent the subsequent operations from ever happening). This is a hierarchy of queues -- one operation creates an entire series of operations in it's own, contained queue. (Two queues: one containing the 'top' operation, which itself contains a queue containing it's children -- but I need to keep that first operation 'alive' until they're all done) – RonLugge May 10 '13 at 03:58
  • @CodaFi to respond to your edit, the point is that the NSOperation creates the additional operations; I need to add them to a queue accessible to it, and it just 'makes sense' to let it handle the entire operation. That way I can have a single MOC responsible for making it an atomic operation -- until the NSOperation's moc saves, the children's save operations are never written to disk. – RonLugge May 10 '13 at 04:00
  • 1
    Yes, and my point is that you've complicated your life by making a *dynamic* 2-D array of operations! It sounds as though your solution is unique enough that you need to either refactor it into a simpler solution (consider how NSObject does things: a "fast" way, and a "slow" way based on locks, which are analogous to your dependent queue problem), or write your own. I don't believe it'd be too hard, as NSOperationQueue just wraps GCD of late. The state machine of NSOperation wasn't designed for dependency or extension - a rather unfortunate shortcoming in your case. – CodaFi May 10 '13 at 04:04
  • Why isn't it possible to simply send a waitUntilFinished in your superoperation's main to the tail operation of the subqueue? – Amin Negm-Awad May 10 '13 at 05:09
  • I agree with @CarlVeazey. I've done exactly this in my code. I had a set of subtasks that I wanted to manage as a singe unit in a higher level queue. I created a concurrent operation and all was good, simple too. It's just a concurrent operation no point inventing something else. – Rory O'Bryan May 10 '13 at 17:00
  • @AminNegm-Awad mostly because the docs clearly state that an operation should never invoke that on itself. – RonLugge May 10 '13 at 20:11
  • Why don't you just call waitUntilAllOperationsAreFinished: in your "parent" NSOperation queue? – Alejandro Benito-Santos May 13 '13 at 12:29

2 Answers2

1

You could send a waitUntilAllOperationsAreFinished message to your child queue before you exit from your operations main method. This is easy, but not a good idea as it blocks a whole thread which is rather wasteful.

A better solution would be to use the dependency system. Create another operation that has a dependency on your main operation. After you create your child operations also add them as a dependency to that new 'finishing' operation.

Sven
  • 22,475
  • 4
  • 52
  • 71
1

as soon as 'main' exits, the NSOperation is going to be marked as 'finished',

This logic is implemented in [NSOperation start], so you just have to override it with your custom logic: calling main, which spawns some additional operations, which after finishing set the state isFinished on the operation.

ilya n.
  • 18,398
  • 15
  • 71
  • 89