November 30, 2020

ShortNote: DispatchQueue, OperationQueue, RunLoop.


Dispatch Queues

Apple’s solution to run code concurrent.

You can add tasks to a dispatch queue and expect it to be executed at some point. There are different types of dispatch queues. One is the SerialQueue. In this type everything will be processed in the same order as it was added to the queue.

The other is the ConcurrentQueue. As the name suggests, tasks can be executed concurrently within this queue.

The great advantage of Dispatch Queues is, that it changes your mental model of concurrent programming. Instead of thinking in threads, you consider it as blocks of work pushed onto different queues, which is a lot easier.

A Note on Main Thread vs. Main Queue

You might be wondering why the Profiler shows "Main Thread" and why we're referring to it as the "Main Queue". If you refer back to the GCD architecture we described above, the Main Queue is solely responsible for managing the Main Thread. The Dispatch Queues section in the Concurrency Programming Guide says that "the main dispatch queue is a globally available serial queue that executes tasks on the application’s main thread. Because it runs on your application’s main thread, the main queue is often used as a key synchronization point for an application."
The terms "execute on the Main Thread" and "execute on the Main Queue" can be used interchangeably.

Operation Queues

Cocoa’s high-level abstraction of GCD is Operation Queues. Instead of a block of discrete units of work you create operations. These will be pushed onto a queue and then executed at the correct order. There are different types of queues: the main queue, which executes on the main thread and the custom queues, which does not execute on the main thread.

Creating an operation can also be done in multiple ways. Either you can create an Operation with a block, or subclass. In case you subclass, don’t forget to call finish, otherwise, the operation will never stop.

class CustomOperation: Operation {
    override func main() {
        guard isCancelled == false else {
            finish(true)
            return }
            
            // Do something
            
        finish(true)
    }
}

A nice advantage about operations is that you can use dependencies. Having operation A depends on operation B results in B not being executed before A.