Reputation: 809
Is it safe to add an NSOperationQueue
to an NSOperation
, and then add this operation to another NSOperationQueue
?
Here is some code to visualize what I am trying to do.
NSOperationQueue *mainQueue = [NSOperationQueue alloc] init];
// Here I declare some NSBlockOperation's, i.e. parseOperation1-2-3
// and also another operation called zipOperation, which includes
// an NSOperationQueue itself. This queue takes the processed (parsed) files
// and write them to a single zip file. Each operation's job is to write the data
// stream and add it to the zip file. After all operations are done,
// it closes the zip.
[zipOperation addDependency:parseOperation1];
[zipOperation addDependency:parseOperation2];
[zipOperation addDependency:parseOperation3];
[mainQueue addOperation:parseOperation1];
[mainQueue addOperation:parseOperation2];
[mainQueue addOperation:parseOperation3];
[mainQueue addOperation:zipOperation];
Upvotes: 3
Views: 1314
Reputation: 1904
I have used this approach and have it running in live code deployed on the App Store. I haven't experienced any issues during development or in the last 2 months since the code has been live.
In my case I had a high level series of operations, some of which contained a set of sub operations. Rather than expose the detail of each sub operation into the high level code, I created NSOperations
which themselves contained NSOperationQueues
and enqueued their own sub operations. The code I ended up with was much cleaner and easier to maintain.
I read extensively into NSOperation
and have not seen any commentary that warns against this approach. I reviewed a lot of information online, the Apple documentation, and WWDC videos.
The only possible "drawback" might be the added complexity of understanding and implementing a Concurrent
operation. Embedding an NSOperationQueue
in an NSOperation
means that operation becomes Concurrent
.
So that's a 'YES' from me.
Additional details about concurrent operations:
An NSOperationQueue
calls the start
method on a normal (non-concurrent) NSOperation
and expects the operation to be finished by the time the start
call returns. For instance some piece of code you supplied to NSBlockOperation
is complete at the end of the block.
If the work will not be finished by the time the start
call returns then you configure the NSOperation
as a Concurrent
operation, so the NSOperationQueue
knows that it has to wait until you tell it that the operation is finished at some later point in time.
For example, concurrent operations are often used to run asynchronous network calls; the start method only starts the network call, which then runs in the background, and calls back to the operation when its finished. You then change the isFinished
property of the NSOperation
to flag that the work is now complete.
So.... Normally when you add operations to an NSOperationQueue
that queue runs those operations in the background. So if you put an NSOperationQueue
inside an NSOperation
then that operations work will be done in the background. Therefore the operation is concurrent
and you need to flag when the internal NSOperationQueue
has finished processing all it's operations.
Alternatively there are some methods on NSOperationQueue
such as waitUntilAllOperationsAreFinished
which could be used to ensure all the work was done before the start
call returns, however these involve blocking threads and I avoided them, you may feel more comfortable with that approach, and making sure you don't have any side effects from blocking threads.
In my case I was already familiar with Concurrent
operations so it was straightforward just to set it up as a Concurrent
operation.
Some documentation about concurrent operations:
Concurrency Programming Guide: Configuring Operations for Concurrent Execution
In this example they are detaching a thread to perform work in the background, in our case we would be starting the NSOperationQueue
here.
Upvotes: 5