Threading Overview:
In general a thread execution is the a task performing by core processors , a task can be a function , a method , a block or anything that require processor(s) to consume it!!!
As a Developer we have 4 options to use Threads:
1) pthreads (exposed by POSIX)
2) NSthread (Apple SDK Object based on pthreads created in Objective-C OSX>= 10.0 and iOS>=2.0)
3) GCD ( Threads C functions OSX>= 10.7 and iOS>=4.3)
4) NSOperation, NSBlockOperation (OSX>= 10.5 and iOS>=4.0)
pthreads is a C syntax which managing lifecycle is up to you , rarely directly used
NSthread : Objective-C syntax managing lifecycle is up to you
Apple encourages developers to use approach 3 & 4 for threading , so we're going to focus on GCD and NSOperation classes family:
Both concept of GCD and NSOperation suit are based on Queuing : in both cases we have two steps in order to create and use Threads 1) create/obtain a Queue 2) submit our task(s) to Queue
NSXXXOperation
NSOperation is an abstract class and not used directly , instead you can use NSBlockOperation , NSInvokeOperation,NSOperationQueue,... but they all rely on the same principal concept except detail differences and ofcourse the fact that NSBlockOperation,... are following OOP pattern.
Example using NSBlockOperation :
let queue = NSOperationQueue()
let operation = NSBlockOperation(block:{()->Void in
doCalcule() //NO UI operation
})
queue.addOperation(readOperation) //start thread
In order to perform any UI operation kind of update ,reloading tableview….etc , you can use a Main Queue and submit it inside your block after your calclculation is over like this
let queue = NSOperationQueue()
let operation = NSBlockOperation(block:{()->Void in
doCalcule() //NO UI operation
NSOperationQueue.mainQueue().addOperationWithBlock({()->Void in
self.mTableView.reloadData() }) //any UI operation here reloading a TableView
})
queue.addOperation(readOperation) //start thread
Other solution is to use completionBlock Other closure which is a property of NSBlockOperation as shown here
let queue = NSOperationQueue()
let operation = NSBlockOperation(block: {()->Void in
doCalcule() //NO UI operation
})
operation.completionBlock = {
let mainQueue = NSOperationQueue.mainQueue()
let uiOperation = NSBlockOperation(block: {()->Void in
self.mTableView.reloadData()
})
mainQueue.addOperation(uiOperation)
}
queue.addOperation(readOperation)
NSOperation and NSBlockOperation are the implementation of GCD in a higher level , GCD is written in C language and is considered as a low level implementation , so in term of OOP programming Apple implemented NSOperation and NSBlockOperation and in many operation it would be preferable to use them over GCD syntax threading
GCD: Grand central dispatch
Remind :
Similar to Android concept in IOS threads are divided to two categories Safe/Unsafe , UIKit components mostely are by deafult not safe, that means access UI elements from a Non-Main Threads are not allowed and raise exceptions.
GCD is designed to centralize all threads task management and threads manipulation by delegating it to system
this way developers are focusing on high level task preparing rather than low level thread manipulation which is a complex task when sync & concurrency come to game!
To use GCD first enqueue or ask for Queue (find lineup for request!) then give your instruction and wait!
1)dispatch_get_global_queue : For a Non-UI or global scoop routine
2)dispatch_get_main_queue: For a UI or Main scoop routine (executed in the context of your main thread of your Application)
example:
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue {
//non-UI Closure todo
};)
In fact the task codes we want to execute on a Thread are the closure we pass to Queue
An example for most commun scenario in this case is when updating your UI after some background operation done
let queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue1 {
//non-UI Closure todo
};)
let queue2 = dispatch_get_main_queue();
dispatch_async(queue2 {
//UI Closure todo
};)
In a real world scenario there will be pereferable to organize your threads in a pool of threads called "Group"
To do that first create a dispatch group:
let group = dispatch_group_create()
then run threads as below
dispatch_group_async(group,
queue1{
firstResult = self.firstCalculation(processedData)
})
dispatch_group_async(group,
queue1{
firstResult = self.secondCalculation(processedData)
})
dispatch_group_notify(group, queue1) {
let results=xxxx //results
//update UI
dispatch_async(queue2 {
//UI Closure todo
};)
dispatch_group_notify : tell the group that if every threads in groups are done notify me!
so the following closure will be executed after all threads in queue are done!
dispatch_group_notify(group, queue) {
//closure body
}
=======================
Mac OS has a extrem safe mechanism for multithreading called GCD, and strongly recommend to not use Threads directly instead you submit your task to system and let him to manage your threads.
You submit it through Dispatch Queues (Queuing is First_In_First_Out)
A serial dispatch queue runs only one task at a time, waiting until that task is complete before dequeuing and starting a new one. By contrast, a concurrent dispatch queue starts as many tasks as it can without waiting for already started tasks to finish.
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
println("This is run on the background queue")
dispatch_async(dispatch_get_main_queue(), { () -> Void in
println("This is run on the main queue, after the previous code in outer block")
})
})