The doc of NSOperationQueue.qualityOfService
says:
This property specifies the service level applied to operation objects added to the queue. If the operation object has an explicit service level set, that value is used instead.
It doesn't mention anything about the relative quality between operation and queue so any combination should work.
However, GCD
has some corresponding features whose docs say:
DISPATCH_BLOCK_ENFORCE_QOS_CLASS
Flag indicating that execution of a dispatch block object submitted to a queue should prefer the QOS class assigned to the block (resp. associated with the block at the time of submission) over the QOS class assigned to the queue, as long as doing so will not result in a lower QOS class.
If NSOperation
and NSOperationQueue
are built on top of GCD
— which I believe is true — it seems the restriction should also apply to them.
So if I add an NSOperation
with a lower qualityOfService
to an NSOperationQueue
will it run with its own lower qualityOfService
or the queue's higher qualityOfService
?
Thanks @das for pointing me to Foundation Release Notes for OS X v10.10.
Ignoring the operation dependency part, rules of QoS between NSOperation
and NSOperationQueue
are exactly the same as that between dispatch block and dispatch queue — though @das points out "the QoS implementation of NSOperationQueue is not built on top of the GCD QoS block primitives" the rules are actually the same, that is to say, NSOperation
's QoS(if lower) will be raise to NSOperationQueue
's QoS. So my initial guess is correct. The doc of NSOperationQueue.qualityOfService
is wrong:
If the operation object has an explicit service level set, that value is used instead.
Here are the detailed explanation of QoS rules of NSOperation
and NSOperationQueue
:
NSOperationQueue qualityOfService
NSOperationQueue has a new qualityOfService property.
You can change the qualityOfService property at any time.
When an operation is added to a queue, the queue's qualityOfService value at that time may affect the effective QOS that the operation will be run at:
- If the queue property has not been set, the operation is unaffected.
- If the queue property has been set to NSQualityOfServiceDefault, the operation is unaffected.
- If the queue property is set to another value, the operation is promoted to the queue's qualityOfService if its promotion QOS is not already at least that level.
If the qualityOfService property of a queue is changed while there are operations in the queue, the effective QOS of operations in the queue will be affected, just as the operations were when added to the queue. Thus, when the qualityOfService property of a queue is changed, all operations in the queue, running or not, have their effective QOS raised up to that level (if they were lower), and future additions to the queue are raised to that level (when they are lower). If the qualityOfService property is lowered from one level to another, only future additions will be affected by that new lower value. Operations' promotion or effective QOS is never lowered by the setting of, or the set value of, a queue's qualityOfService.
When an operation is added to a queue, the operation's effective QOS values at that time may affect the effective QOS of the operations which are already in the queue ("ahead of it"):
- The operations already ahead in the queue of the newly added operation, running or not, are promoted to the effective QOS of the operation being added.
Thus, if a high QOS operation is added to a queue, operations already in the queue are raised up to that level (if they were lower). Operations added after that high-QOS operation are not affected by its presence in the queue.
The meaning and interaction of operation promotion QOS and effective QOS is discussed in the section on NSOperation qualityOfService.
NSOperationQueues do not infer any QOS from any execution context.
If the (dispatch_queue_t) underlyingQueue property of an NSOperationQueue is set, qualityOfService property values of NSOperationQueues and NSOperations have no effect. The effective QOS of operations run by that queue is determined by the state of the dispatch_queue_t.
NSOperation qualityOfService
NSOperation has a new qualityOfService property.
You can change the qualityOfService property at any time.
There are various real and virtual QOS values that relate to how an operation runs:
- The qualityOfService property value
- An inferred QOS
- The promotion QOSes
- The effective QOS
When an operation object is created, an inferred QOS value is computed from the execution context:
- If either:
- the operation is being created in the execution context of another operation (already running on that thread); or
- the operation is being created in the execution context of a certain NSProcessInfo API; then the nearest one of those to the current activation frame of the call stack of the current thread is used as the inferred QOS of the new operation:
- that operation's effective QOS at the time it started running;
- the NSProcessInfo API's values are mapped to a QOS value.
- If the operation is being created on the main thread, the inferred QOS is NSQualityOfServiceUserInitiated.
- Otherwise, the current thread's QOS (which may be none) is read and used as the inferred QOS of the new operation.
An operation can be promoted (have promotion QOSes applied to it) in several contexts:
- When the operation is added to a queue, or when the qualityOfService property of the queue the operation is in is changed
- (as discussed in the NSOperationQueue section)
- When a different operation is added to a queue that the operation (in question) is already in
- (as discussed in the NSOperationQueue section)
- When a different later (after the operation in question) operation in the same queue has its effective QOS raised
- the effective QOS of the other operation promotes the operation
- When a different operation (a dependee) becomes dependent upon the operation in question
- the effective QOS of the dependee promotes the operation
- When a dependee operation has its effective QOS raised
- the new effective QOS of the dependee promotes the operation
- When the operation is waited upon, with the -waitUntilFinished method, or indirectly when the operation's queue's -waitUntilAllOperationsAreFinished method, is used
- if the waiting thread is the main thread, the promotion QOS is taken to be NSQualityOfServiceUserInteractive;
- otherwise if the waiting is done in the execution context of another operation, its effective QOS promotes the operation;
- otherwise the QOS of the current thread promotes the operation.
These are all collectively called the promotion QOSes; or for the MAX() of all of them, just promotion QOS.
These various values are put together into the effective QOS. The effective QOS is the MAX() of all of these QOS values: {the inferred QOS, the promotion QOSes, the qualityOfService property value}, with these qualifications:
- If the operation's qualityOfService property has been explicitly set to anything, even NSQualityOfServiceDefault, the inferred QOS is ignored.
- If the operation's qualityOfService property has not been explicitly set to anything, it is ignored (as if no value exists).
- All QOS values of NSQualityOfServiceDefault are ignored.
- If there are no QOS values after all that ignoring, the effective QOS is NSQualityOfServiceDefault.
Thus, for example, if an operation is waited upon, its effective QOS may be raised by the waiting context, which may recursively raised all of its dependent operations and all operations ahead of it in the queue (and so on recursively outward in the tree of these relationships).
An operation's qualityOfService property value has no effect if the operation is started manually, rather than being put in an NSOperationQueue, unless the code which is starting it reads out that value and makes some appropriate use of it; that is outside the purview of Foundation.