GCD
Thread -> GCD -> Operation + OperationQueue(life cycle, dependencies between different queues, cancel)
[Sync vs Async]
[iOS Thread safe]
Grand Central Dispatch(GCD) libdispatch operates on dispatch queues DispatchQueue. It works by.first in first out(FIFO) order
DispatchQueue.<queue>.<sync/async> means run a <sync/async> task on the <queue>
Queue can be serial or concurrent. Concurrent(it is more parallel) allows to process with several task simultaneously while serial - one after another. Concurrent tasks are started at that order which they were added but can be finished at different order
GCD supports:
- main queue- serial queue on a main thread of app which is used to working with UI
- global queue- concurrent queues which are shared between whole iOS operation system
- private queue- serial/concurrent queues in app scope
Main Queue
//Thread.current.qualityOfService = .userInitiated
DispatchQueue.main
Global Queues
DispatchQueue.global(qos: DispatchQoS.QoSClass = .default)
Private Queue
DispatchQueue(label: String, qos: DispatchQoS = .unspecified, attributes: DispatchQueue.Attributes = [], autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = .inherit, target: DispatchQueue? = nil)
//Serial Queue
DispatchQueue(label: "first.myApp.com") 
//Concurrent Queue - attributes: .concurrent
DispatchQueue(label: "second.myApp.com", attributes: .concurrent)       
Quality of Service(QoS)
Every Queue has qos which determines a priority of it
- userInteractive- the highest priority - speed over energy. Task result of which are reflected on UI. Task should be small to produce fast result. For example animation
- userInitiated- task which is started by user and result of which is async. For example loading data/opening local file to show result on UI
- utility- long-running calculations up to several minutes like saving data to file...
- background- the lowest priority - energy over speed. Very long-running computations from several minutes - like sync data.
Additional QoS which you should avoid:
- default - priority is between .userInitiated and .utility
- unspecified - uses a QoS of outer thread(inherits the QoS of the caller). For example when you call it from main thread thread.qualityOfService == .userInitiated
func examineQoS(qos: DispatchQoS) {
    let queue = <create_queue>
    queue.async {
        print("""
                QoS
                qos: \(qos.qosClass)
                queue.qos: \(queue.qos.qosClass)
                thread.qualityOfService: \(Thread.current.qualityOfService)
                """)
    }
}
let queue = DispatchQueue.global()
let queue = DispatchQueue(label: "mySerial")
let queue = DispatchQueue(label: "myConcurrent", attributes: .concurrent)
//qos == .default
//queue.qos: unspecified
//thread.qualityOfService: .userInitiated
let queue = DispatchQueue.global(qos: qos.qosClass)
    
//qos: default
//queue.qos: unspecified
//thread.qualityOfService: .userInitiated
//qos: unspecified
//queue.qos: unspecified
//thread.qualityOfService: .userInitiated
//Others
//qos == queue.qos == thread.qualityOfService
let queue = DispatchQueue(label: "mySerial", qos: qos)
let queue = DispatchQueue(label: "myConcurrent", qos: qos, attributes: .concurrent)
//qos: default
//queue.qos: default
//thread.qualityOfService: .default
//qos: unspecified
//queue.qos: unspecified
//thread.qualityOfService: .userInitiated
//Others
//qos == queue.qos == thread.qualityOfService
 
- Count of worker threads are depended on OS conditions. There are no run loop[About] for worker thread.
sync/async
sync - block a current thread and wait when it will be finished on a specified queue
async - do not block a current thread and send an execution block of code to the specificified queue
//deadline in seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
    // logic
}
Common mistake: deadlock
If you call DispatchQueue.main.sync on a main thread - the app will be frozen because the calling DispatchQueue.main.sync starts waiting immediately when the dispatched block is finished (dispatched block is not started)
Some notes:
- DispatchWorkItem- delaying/cancelling/prioritise a task inside- Queueor- DispatchGroup
- DispatchGroupif you are going to execute several async tasks with a single callback even on different queues. All these task should be grouped.- DispatchGroupcontains thread safe counter and when it equals 0- notifyis called
//create group
let group = DispatchGroup()
//case 1
DispatchQueue.<queue>.async(group: group) //
//case 2 - manual
group.enter() //<- +1
DispatchQueue.global().async { 
    //logic
    group.leave() //<- -1
}
//notification
group.notify(queue: <callback_queue>) {
    //logic             
}
- Barrierflag inside concurrent queue for sync/async task guaranties that there is no- race condition[About]. The best place for it is custom queue because does not block any others global tasks:
customQueue.async(flags: .barrier) { 
    //logic
    someProperty = someValue
}
- all task which were started are finished
- Single Barrier task
- Executing all other tasks in the queue
thread safe operation can be reached through Barrier in concurrent  queue for shared variable:
- read - sync operation on concurrent queue
- write - async operation with barrier
[Thread safe singleton]