Yes, I know. There are a lot of questions and answers about the
NSOperation world but I'm still having some doubts. Il' try to explain my doubts with a two parts question. They are related each other.
In the SO post nsoperationqueue-and-concurrent-vs-non-concurrent, Darren wrote that
A "concurrent" operation is concurrent on its own; it doesn't need
NSOperationQueue to create a thread for it.
But searching a little bit, I've found that a
NSOperation, even if it is declared concurrent (by means of overriding the
isConcurrent method such as it returns
YES), can be added to
NSOperationQueue. What does this mean? If I add a concurrent
NSOperation to a queue, what is going on under the hood? On the contrary, what happens if I use a concurrent operation as is (without adding it to a queue)?
The note taken from Apple doc is clear:
...operation queues ignore the value returned by
isConcurrent and always call the start method of your operation from a
separate thread. ...In general, if you are always
using operations with an operation queue, there is no reason to make
Then, I'm really interested about using async pattern in
NSOperation. I've found a good tutorial by Dave Dribin (concurrent operations). I got the overall meaning of his post.
You cannot use an async pattern (e.g. using an async
NSURLConnection request) since delegates could not be called. When the
main finishes the operation is removed. The solution is to override the
start method to control the operation lifecycle...And dealing with run loops could be a pain.
Now, trying to understand his post, my doubt is about the need to run the
start method in the main thread.
if (![NSThread isMainThread])
[self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
// other code here...
When dealing with asynchronous APIs, we can begin the asynchronous
call on the main thread in start and keep the operation running until
Could you explain me why?
Thank you in advance.
In my opinion, the
isConcurrent property of
NSOperation is confusingly named. It really means "is-asynchronous". That is, when its
-start is invoked, does it return quickly whether or not the operation has run to completion (asynchronous)? Or does it not return until the operation has run to completion (synchronous)?
As Apple's docs state, this doesn't much matter when an operation is queued to an
NSOperationQueue, since the queue invokes it on a worker thread, regardless. If it is synchronous, then that worker thread will be devoted to that operation until it completes. If it is asynchronous, then
-start will return before the operation has completed and the worker thread can go on to do other work.
The question is, how does an asynchronous
-start method make sure the work of the operation continues? That could entail spawning a separate thread to do the work, but that's silly. Better to let
NSOperationQueue handle the threading.
More likely, it uses a run loop source that's driven by external events.
NSURLConnection in its asynchronous mode is that sort of thing. But, in that case, it has to be sure the thread on which it schedules the run loop source a) will stick around, and b) will run its run loop. The worker threads of
NSOperationQueue can't be relied upon to do either.
Again, you could create your own thread for each such operation specifically to stick around and run its run loop, but that's unnecessary and, again, offers no advantage over leaving the operation synchronous and queuing it.
One thread which you already know will stick around and run its run loop is the main thread. So, it is often best to schedule the run loop source on the main thread's run loop. The only thing to be careful of is that, in response to the external events that trigger the run loop source's handlers, you don't do any long-running work on the main thread. So, for example, when
NSURLConnection calls your delegate methods with received data, you don't do expensive calculations on that data – or, if you have to, move that expensive computation to another thread.
Another possibility, a middle ground, is to create one thread of your own to be the worker for many asynchronous operations. So, rather than using the main thread or a thread per operation, you use a single thread whose job is just to sit around parked in its run loop. All of your asynchronous operations would schedule themselves on that thread's run loop. There's not really much need for or advantage to this approach, though.
I think Dave addresses his motivation for shunting the start method onto the main thread in the post:
Update 2009-09-13: This is no longer true as of 10.6. The start method is always called on a background thread as of 10.6. To work properly with main-thread only and asynchronous APIs that rely on the run loop, we need to shunt our work over to the main thread. More on this in a followup post.