Quality of service classes are an integral component of Grand Central Dispatch. You know from the previous episode what they are and how to use them. In this episode, you learn how to efficiently apply quality of service classes.
Efficiently Applying Quality of Service Classes
Even though you understand what quality of service classes are and how to use them, it's equally important to understand how Grand Central Dispatch handles quality of service classes. This is essential if you want to avoid hard to find bugs and if your goal is writing performant applications.
Consider the following example. We create a dispatch work item and set its quality of service class to background. What happens if the dispatch work item is submitted to a dispatch queue with the utility quality of service class? Should Grand Central Dispatch honor the quality of service class of the dispatch work item or does it enforce the quality of service class of the dispatch queue?
When Grand Central Dispatch detects conflicting quality of service classes, it infers the quality of service class based on a number of rules. Understanding and becoming familiar with these rules is essential if you want to efficiently apply quality of service classes. This isn't the easiest of topics because Grand Central Dispatch defines different sets of rules for synchronous and asynchronous execution.
Synchronous Execution
For synchronous execution, Grand Central Dispatch defaults to the quality of service class of the block of work or, if no quality of service class is set, the quality of service class of the submitting thread. Remember that synchronously executing work blocks the execution of the calling thread and it's therefore important that the work completes as soon as possible.
If this seems odd or surprising, then consider the following example. If a task is submitted from a background thread to a dispatch queue for synchronous execution, then the background thread, the submitting thread, is blocked until the task has finished executing. That's inherent to synchronous execution. We discussed this several times in this series.
To unblock the submitting thread, it's essential that the task is executed as soon as possible to continue execution of the background thread. If the block of work doesn't specify a quality of service class, then it makes sense for the block of work to inherit the quality of service class of the submitting thread, the background thread in this example.
This rationale makes even more sense if we consider the scenario in which a block of work is scheduled for synchronous execution from the main thread. Whenever that happens, it is crucial that the block of work is executed as soon as possible to ensure that the main thread is unblocked and can continue its execution.
Asynchronous Execution
The rules are a bit more complex for asynchronous execution. Grand Central Dispatch defaults to the quality of service class of the dispatch queue to which the block of work is submitted or, if the dispatch queue doesn't have a quality of service class, it inherits the quality of service class of the immediate global target dispatch queue. You can ignore the latter for now since we won't cover target dispatch queues in this episode.
If the dispatch queue doesn't define a quality of service class, then the quality of service class of the block of work is used or it is inferred from the submitting thread. This seems similar to synchronously executing a block of work. There's an important difference, though.
Consider the following example. If a block of work is submitted from the main thread to a dispatch queue for asynchronous execution, then the block of work inherits the quality of service class of the submitting thread if the dispatch queue and the block of work don't specify a quality of service class. This is also known as quality of service propagation. Since the block of work is submitted from the main thread, the inferred quality of service class is user interactive, the quality of service class of the main thread.
There's one exception to this rule, though. The user interactive quality of service class is automatically translated to the user initiated quality of service class if a block of work is asynchronously executed. The user interactive quality of service class isn't propagated.
Why is that? Work that has a user interactive quality of service class is high priority work and the amount of work that is executed as user interactive should be kept to a minimum. That is why the user interactive quality of service class is automatically translated or lowered to the user initiated quality of service class. This safeguard prevents the user interactive quality of service class from accidentally being propagated to various places in the application.
There are a few subtle details I want to point out before moving on. First, if a block of work is scheduled for asynchronous execution, then the quality of service class of the dispatch queue is used by default. The quality of service class of the block of work or the submitting thread is only used for dispatch queues that don't explicitly define a quality of service class. This is possible if you're dealing with a legacy API or if the dispatch queue doesn't serve a single purpose in the application.
Second, remember from the previous episodes that it's also possible to tweak the behavior of a block of work through a flag. The inheritQoS flag indicates that the submitted block of work prefers the quality of service class it inherits from the dispatch queue it is submitted to or the thread it is submitted from. The enforceQoS flag results in the opposite behavior. It ensures that the quality of service class of the block of work takes precedence over that of the dispatch queue it is submitted to or the thread it is submitted from. The enforceQoS flag only raises the quality of service class of the block of work.
Priority Inversion
Correctly and efficiently applying quality of service classes can be complex and you run the risk of introducing bugs that result in performance issues. This is something you need to avoid. The good news is that Grand Central Dispatch helps avoid performance issues to some extent. Let me explain how that works.
The scenario I want to discuss is commonly referred to as priority inversion. It may sound complex, but it really isn't. Consider the following example. Your application creates a serial dispatch queue and submits a block of work with the background quality of service class to the dispatch queue. It then submits a block of work to the dispatch queue with a user initiated quality of service class. Remember that a dispatch queue is a FIFO queue and a serial dispatch queue executes one block at a time. This means that the second block of work, the high priority work, is waiting for the first block of work to complete, the low priority work. The priorities of the blocks of work are inverted hence the term priority inversion. I hope it's clear that such a scenario needs to be avoided. The execution of the high priority work is waiting for the low priority work to complete.
Priority inversion is an interesting problem and many developers are unaware of the issue. They may experience performance issues they can't explain and priority inversion often goes unnoticed. The good news is that Grand Central Dispatch understands this problem and knows how to cope with priority inversion, but there are limitations to what it can do. Consider the previous example. The high priority work is waiting for the low priority work to complete. Grand Central Dispatch tries to alleviate the issue by temporarily raising the quality of service class of the low priority work that blocks the execution of the high priority work.
Even though Grand Central Dispatch tries to resolve priority inversions whenever it can, there are limitations. I hope it's clear that the solution of Grand Central Dispatch to resolve priority inversions is a temporary workaround. Raising the quality of service class of low priority work is something that defeats the purpose of quality of service classes. The message is simple. Avoid priority inversions as much as possible by efficiently applying quality of service classes.
Debugging Quality of Service Classes
Debugging Grand Central Dispatch can be challenging. To better understand how your application uses Grand Central Dispatch, Xcode ships with a number of instruments that offer a window into the inner workings of Apple's concurrency library. Let's revisit the project from earlier in this series to illustrate how Xcode can help debug issues related to Grand Central Dispatch. I have interrupted the execution of the application and opened the Debug Navigator on the left.

Stack Traces
The Debug Navigator contains a lot of information and it can be overwhelming if you don't know what you should be looking for. Let's start by inspecting the running process by thread. Click the button in the top right and choose View Process by Thread.

We're interested in the main thread. Xcode shows the dispatch queue that submitted the block of work to the main thread. The label of the main dispatch queue is equal to com.apple.main-thread and, as you may remember from earlier in this series, the main dispatch queue is a serial dispatch queue. The Debug Navigator shows the stack trace of the currently running code and it also shows the historical stack trace that was captured at the moment the block of work was enqueued. This is very useful and it makes debugging asynchronous code a lot easier. Notice that the icons of the live stack trace are colored whereas the icons of the historical stack trace are greyed out.

The Debug Navigator can also show the running process by queue. Click the button in the top right and choose View Process by Queue.

The Debug Navigator lists the dispatch queues and the blocks that are running or pending execution. Notice that the global dispatch queue with the utility quality of service class lists a number of running blocks. Each of these blocks is being executed on a background or worker thread.

The stack trace of the main dispatch queue shows that the block of work to load the data for the remote resource was enqueued from the main thread. To update the image view of the table view cell, a block of work was submitted to the main dispatch queue from a background thread.

CPU Report
Click the CPU tab in the Debug Navigator to bring up the CPU report. The report that Xcode generates offers an overview of how your application takes advantage of Grand Central Dispatch and how quality of service classes are being applied. The report shows the live threads and the quality of service class of each thread. The main thread has a quality of service class of user interactive. The background or worker threads the application uses to load the data for the remote resources have a quality of service class of utility.

What's Next?
Grand Central Dispatch is a powerful concurrency library, but I want to emphasize how important it is that you understand how Apple's concurrency library works under the hood. Quality of service classes are an integral component of Grand Central Dispatch and it's essential that you correctly apply them when working with Grand Central Dispatch or any other technology that relies on it, such as operations and operation queues.