The reason for this question is because of the reactions to this question.
I realized the understanding of the problem was not fully there as well as the reason for the question in the first place. So I am trying to boil down the reason for the other question to this one at it's core.
First a little preface, and some history, I know NSOperation(Queue) existed before GCD, and and they were implemented using threads before dispatch queues.
The next thing is that you need to understand is that by default, meaning no "waiting" methods being use on operations or operation queues (just a standard "addOperation:"), an NSOperation's main method is executed on the underlying queue of the NSOperationQueue asynchronously (e.g. dispatch_async()).
To conclude my preface, I'm questioning the purpose of setting NSOperationQueue.mainQueue.maxConcurrentOperationCount to 1 in this day and age, now that the underlyingQueue is actually the main GCD serial queue (e.g. the return of dispatch_get_main_queue()).
If NSOperationQueue.mainQueue already executes it's operation's main methods serially, why worry about maxConcurrentOperationCount at all?
To see the issue of it being set to 1, please see the example in the referenced question.
It's set to 1 because there's no reason to set it to anything else, and it's probably slightly better to keep it set to 1 for at least three reasons I can think of.
Reason 1
Because
NSOperationQueue.mainQueue
'sunderlyingQueue
isdispatch_get_main_queue()
, which is serial,NSOperationQueue.mainQueue
is effectively serial (it could never run more than a single block at a time, even if itsmaxConcurrentOperationCount
were greater than 1).We can check this by creating our own
NSOperationQueue
, putting a serial queue in itsunderlyingQueue
target chain, and setting itsmaxConcurrentOperationCount
to a large number.Create a new project in Xcode using the macOS > Cocoa App template with language Objective-C. Replace the
AppDelegate
implementation with this:If you run this, you'll see that operation 1 doesn't start until operation 0 has ended, even though I set
operationQueue.maxConcurrentOperationCount
to 100. This happens because there is a serial queue in the target chain ofoperationQueue.underlyingQueue
. ThusoperationQueue
is effectively serial, even though itsmaxConcurrentOperationCount
is not 1.You can play with the code to try changing the structure of the target chain. You'll find that if there is a serial queue anywhere in that chain, only one operation runs at a time.
But if you set
operationQueue.underlyingQueue = concurrentQueue
, and do not setconcurrentQueue
's target toserialQueue
, then you'll see that 64 operations run simultaneously. ForoperationQueue
to run operations concurrently, the entire target chain starting with itsunderlyingQueue
must be concurrent.Since the main queue is always serial,
NSOperationQueue.mainQueue
is effectively always serial.In fact, if you set
NSOperationQueue.mainQueue.maxConcurrentOperationCount
to anything but 1, it has no effect. If you printNSOperationQueue.mainQueue.maxConcurrentOperationCount
after trying to change it, you'll find that it's still 1. I think it would be even better if the attempt to change it raised an assertion. Silently ignoring attempts to change it is more likely to lead to confusion.Reason 2
NSOperationQueue
submits up tomaxConcurrentOperationCount
blocks to itsunderlyingQueue
simultaneously. Since themainQueue.underlyingQueue
is serial, only one of those blocks can run at a time. Once those blocks are submitted, it may be too late to use the-[NSOperation cancel]
message to cancel the corresponding operations. I'm not sure; this is an implementation detail that I haven't fully explored. Anyway, if it is too late, that is unfortunate as it may lead to a waste of time and battery power.Reason 3
As with mentioned with reason 2,
NSOperationQueue
submits up tomaxConcurrentOperationCount
blocks to itsunderlyingQueue
simultaneously. SincemainQueue.underlyingQueue
is serial, only one of those blocks can execute at a time. The other blocks, and any other resources thedispatch_queue_t
uses to track them, must sit around idly, waiting for their turns to run. This is a waste of resources. Not a big waste, but a waste nonetheless. IfmainQueue.maxConcurrentOperationCount
is set to 1, it will only submit a single block to itsunderlyingQueue
at a time, thus preventing GCD from allocating resources uselessly.