Difference Between NSOperation and Dispatch Queues

Shayan Mehranpour
CodeX
Published in
8 min readJul 25, 2021

Concurrency means multiple computations are happening at the same time. Concurrency is everywhere in modern programming, whether we like it or not:

  • Multiple computers in a network
  • Multiple applications running on one computer
  • Multiple processors in a computer (today, often multiple processor cores on a single chip)

In fact, concurrency is essential in modern programming:

  • Web sites must handle multiple simultaneous users.
  • Mobile apps need to do some of their processing on servers (“in the cloud”).
  • Graphical user interfaces almost always require background work that does not interrupt the user. For example, Eclipse compiles your Java code while you’re still editing it.

Being able to program with concurrency will still be important in the future. Processor clock speeds are no longer increasing. Instead, we’re getting more cores with each new generation of chips. So in the future, in order to get a computation to run faster, we’ll have to split up a computation into concurrent pieces.

So before starting writing any concurrency code, think first why do you need concurrency and which API do you need to use to solve this problem? In iOS we have different APIs that can be used. In this tutorial we will talk about two of the most commonly used APIs — Operation and Dispatch Queues.

What do You Need to Know about Concurrency?

In this tutorial, I will explain everything you need to understand concurrency and relieve all fear you have about it. First we recommend to have a look at blocks (closures in Swift) as they are used heavily in concurrency APIs. Then we will talk about Dispatch Queues and OperationQueues. I will walk you through each of the concurrency concepts, their differences, and how to implement them.

GCD (Grand Central Dispatch)

Grand Central Dispatch is used in iOS to introduce concurrency and parallelism in the iOS applications, so that multiple heavy tasks are performed in the background, resulting in the smooth experience of the application usage, as the main thread is unaffected by the heavy background tasks.

GCD is introduced in iOS 4 to avoid the serial execution of the tasks as serial execution could be a pain.

Serial execution is not an ideal thing as one task is waiting for the processor until another task gets completed. What if the currently executing task is much heavy and the waiting task is pretty much small. Thus, what we get is a lot of waiting for our small task until the big one is finished.

Dispatch Queues

Dispatch queues are an easy way to perform tasks asynchronously and concurrently in your application. They are queues where tasks are being submitted by your app in form of blocks (Blocks of codes). There are two varieties of dispatch queues: (1) serial queues, & (2) concurrent queues. Before talking about the differences, you need to know that tasks assigned to both queues are being executed in separate threads than the thread they were created on. In other words, you create blocks of code and submit it to dispatch queues in the main thread. But all these tasks (Blocks of codes) will run in separate threads instead of the main thread.

What is a serial queue?

A serial Dispatch Queue performs only one task at the time. Serial queues are often used to synchronize access to a specific value or resource to prevent data races to occur.

What is a concurrent queue?

A concurrent queue allows us to execute multiple tasks at the same time. Tasks will always start in the order they’re added but they can finish in a different order as they can be executed in parallel. Tasks will run on distinct threads that are managed by the dispatch queue. The number of tasks running at the same time is variable and depends on system conditions.

Demo Project

Here in our demo project we have a downloader for downloading some images to show you difference between GCD and Operation.

First of all create a simple application. Based on your knowledge you can choose Storyboard or SwiftUI. In this demo we move forward with Storyboard Interface.

After creating the project, open Main.storyboard and put four UIImageView, one UISlider and UIButton like below image.

Now create outlets for images and action for Download button and slider.

For downloading images I wrote a simple downloader and put 4 image url to download and show them in our UIImageViews.

First of all I wrote regularDownload function and call this func when download button pressed. You will see you can’t change the value of slider because all of the images are downloading in main thread.

Each downloader is considered as a task and all tasks now are being performed in the main queue. Let’s now get a reference for one of the global concurrent queue which is the Default priority queue.

We first get a reference to the default concurrent queue using DispatchQueue.global, then inside the block we submit a task which is to download the first image. Once the image download completes, we submit another task to the main queue to update the image view with the downloaded image. In other words, we put the image download task in a background thread, but execute the UI related tasks in the main queue.

If ou do the same for the rest of the images, your code should look like this:

Now if you change regularDownload with concurrentQueue function, the app should work faster and while downloading the images you can change the value of slider.

Using Serial Dispatch Queues

This time around we will use a serial queue for downloading the images. When using serial queues, you need to pay close attention to which serial queue you are referencing. Each app has one default serial queue, which is actually the main queue for the UI. So remember that when using serial queues, you must create a new queue, otherwise you will be executing your tasks while the app is attempting to execute the tasks for updating the UI. This will cause errors and delays that destroy the user experience. After the change, the code will look like this:

Now if you change concurrentQueue with serialQueue function, the app should work same before but you should notice two things:

  1. It take a bit long to download the images, as compared to the case of concurrent queue. The reason is that we only load one image at a time. Each task waits for the previous task to finish before being executed.
  2. Images are loaded in order image1, image2, image3, and image4. That’s because the queue is a serial queue that executes one task a time.

Operation Queues

Unlike GCD, Operation Queues don’t conform to the First-In-First-Out order. Here are how operation queues are different from dispatch queues:

  1. In operation queues, you can set priority for your operations and also you can add dependencies to the operations which means you can define that some operation execute only after the completion of other operations.
  2. Operation queues operate concurrently and if you want to use them as serial queue you should handle it with adding dependencies.

Now let’s rewrite our project demo but this time we’ll use OperationQueues. First declare the variable below in the ViewController class:

var queue = OperationQueue()

Next, add the operationQueue method and change it with serialQueue:

As you see in the above code, you use the method addOperation to create a new operation with the given block (or as we call it in Swift, a closure). It’s very simple, isn’t it? To perform a task in the main queue, instead of calling DispatchQueue.main.async as when using GCD, we can do the same from OperationQueue (OperationQueue.main) and submit the operation you want to execute in the main queue.

You can run the app to have a quick test. If the code was entered correctly, the app should be able to download the images in background without blocking the UI.

In the previous example, we used the method addOperation to add operation in the queue. Let’s see how we can use BlockOperation to do the same, but at the same time, giving us more functionalities and options such as setting completion handler.

Now when the operation is done, the completion handler will be called. For simplicity, we just log a simple message to indicate the operation is done. If you run the demo, you would see something like this in console:

Operation 4 completedOperation 3 completedOperation 1 completedOperation 2 completed

Canceling Operations

As I mentioned before, BlockOperation allows you to manage the operations. Now let’s see how to cancel an operation. To do that, first add another button below download button and set it’s title Cancel. For showing the cancel operation we make dependency between Operation #2 and Operation #1 and another dependency between Operation #3 and Operation #2. Now Operation #2 will start after finishing of Operation #1, and same for the other dependency. Now for cancelling the operations you need to call cancelAllOperations().

@IBAction func cancelBtnPressed(_ sender: Any) {    self.queue.cancelAllOperations()}

Now add the dependencies and change the completion block for Operation #1 like below:

operation2.addDependency(operation1)operation3.addDependency(operation2)operation1.completionBlock = {    print("Operation 1 completed, cancelled: \(operation1.isCancelled)")}

Now if you run the project and see the result always Operation #1 or #4 will execute first and after Operation #1 completion, Operation #2 execute and after finishing of Operation #2 the you will see Operation #3 exsecution.

After you hit the Download button, press the Cancel button. This will cancel all operations after operation #1 completes. Here is what happens:

Cancelling will never effect on operation #1 but if you hit Cancel button quick enough, operation #2 is cancelled and image #2 is not downloaded so because of dependency between operation #2 and #3 third image is not downloaded too but operation #4 didn’t have any dependency and run concurrently.

I hope you got a better understanding of the difference between GCD and Operation Queues in Swift and will be able to use and integrate them in your future projects.

Also you can find complete source code here in this concurrency repository.

--

--