Closure
Closure is a first class citizen of the language
- assign them to variables or constants,
- put then inside arrays or dictionaries
- return them as the result of a function or closure
- and even receive them as parameters of another function or closure!
let f = { (x:Int) -> Int in
return x + 42
}
f(89)
let closures = [
f, // our previous closure
{(x:Int) -> Int in return x * 2}, // a new Int -> Int closure
{x in return x - 8}, // no need for the type of the closure!
{(x:Int) in x * x}, // no need for return if only one line
{$0 * 42} // access parameter by position instead of name
]
for fn in closures {
fn(42)
}
Functions and closures are the same thing
Actually, when the compiler finds a function declaration such as foo, it will take the following steps:
- Create a closure that takes an Int and returns 42 plus that Int.
- Then, assigns this closure to a constant called foo
func foo(x:Int) -> Int {
return 42 + x
}
let bar = { (x: Int) -> Int in
return 42 + x
}
add a few functions to an array and then call them
func larry(x:Int) -> Int {
return x * x
}
func curly(n:Int) -> Int {
return n * (n - 1)
}
func moe(n:Int) -> Int {
return n * (n - 1) * (n - 2)
}
var stooges = [larry, curly, moe]
for stooge in stooges {
stooge(42)
}
stooges.append(bar)
for each in stooges {
each(42)
}
Typealias & Capture value
typealias IntMaker = (Void) -> Int
Functions and closure capture variables defined before the closure or function is defined.
func makeCounter() -> IntMaker {
var n = 0
// Functions within functions?
// Yes we can!
func adder() -> Integer {
n = n + 1
return n
}
return adder
}
let counter1 = makeCounter()
let counter2 = makeCounter()
counter1() //1
counter1() //2
counter1() //3
each closure has its own copy of the environment (the values of all variables that were captured).
counter2() //1
GCD
Hiding threads
Threads are inherently complex and dangerous
GCD makes asynchronous programming easier and safer by hiding threads within the concept of a queue of closure from the developer
Blades are inherently dangerous ,add handles to make for safer knives
Types of queues
Serial or synchronous queue - predictable
Every closure gets to run when it reaches the end of the queue
The order in which closures run is predictable
Still have concurrency ,each queue will run on its own threads and will not block each other
Concurrent or Asynchronous queue - unpredictable
Have several threads pickup closures at any point of the queue for running
The order in which closures run is not predictable
Main queue
handles the UI of your app
You should never run code that blocks on this queue
Main function in GCD
create a queue
Represents a queue
dispatch_queue_t
Ceate a queue from scratch,To specify serial, just pass dispatch_queue_serial or nil
dispatch_queue_create(name,serial/concurrent)
Global queues
Use existing one queue instead of creating new queues
QOS_CLASS_USER_INTERACTIVE //top priority
QOS_CLASS_USER_INITIATED //regular priority
QOS_CLASS_BACKGROUND //low
QOS_CLASS_UTILITY //lowes
access those global queues with function
dispatch_get_global_queue
Main queue
get the main queue
dispatch_get_main_queue()
Add a closure to a queue
- the code inside the queue will run sometime in the immediate future
- the exact time will depend on how many closures already waiting in the queue and the priority of the queue
- dispatch_async adds your closures to each queue and return imediately
Add a closure to a queue
dispatch_async ()
Example 1
let q = dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE,0)
//Imediately return and add the block to the q ,then run in the near future
dispatch_async(q){
() -> Void in
print("tic")
}
print ("tac") //this run first
Example 2
//cannot predict order the code run below of each queue
let q1 = dispatch_queue_create("queue1",nil)
let q2 = dispatch_queue_create("queue1",nil)
let q3 = dispatch_queue_create("queue1",nil)
dispatch_async(q1){ ()->Void in
print(1)
}
dispatch_async(q2){ ()->Void in
print(2)
}
dispatch_async(q3){ ()->Void in
print(3)
}
print("end") //run first
UIKIT & CoreData
when the framework can run in the background it is said to be thread safe
cannot run anything from UIKit in the background
//maybe crash , uikit should run in Main queue
let downloadQueue = dispatch_queue_create("download",nil)
dispatch_async(downloadQueue){()-> Void in
let imgData = NSData (contentsOfURL:url!)
let image = UIImage(data:imgData!)
self.photoViewimage = image
}
Synchronous download
// Get the URL for the image
let url = NSURL(string: BigImages.seaLion.rawValue)
// Obtain the NSData with the image
let imgData = NSData(contentsOfURL: url!)
// Turn it into a UIImage
let image = UIImage(data: imgData!)
// Display it
photoView.image = image
Refactor code by if-let
if let url = NSURL(string: BigImages.seaLion.rawValue),
let imgData = NSData(contentsOfURL: url),
let image = UIImage(data: imgData){
// Display it
photoView.image = image
}
Asynchronous download
Not all of UIkit is thread and save.There are a few exceptions, such as UIImage
All the visual stuff, the views are unsaved
if name end view, it must be main queue
@IBAction func simpleAsynchronousDownload(sender: UIBarButtonItem) {
let url = NSURL(string: BigImages.shark.rawValue)
// create a queue
let downloadQueue = dispatch_queue_create("download", nil)
// add a closure that encapsulates the blocking operation
// run it asynchronously: some time in the near future
dispatch_async(downloadQueue) { () -> Void in
// Obtain the NSData with the image
let imgData = NSData(contentsOfURL: url!)
// Turn it into a UIImage
let image = UIImage(data: imgData!)
// Run the code that updates the UI in the main queue!
dispatch_async(dispatch_get_main_queue(), { () -> Void in
// Display it
self.photoView.image = image
})
}
}
Completion Closure
It is a good practice to make sure that always all your completion handlers run in the main queue
func withBigImage(completionHandler handler: (image: UIImage) -> Void){
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) { () -> Void in
// get the url
// get the NSData
// turn it into a UIImage
if let url = NSURL(string: BigImages.whale.rawValue), let imgData = NSData(contentsOfURL: url), let img = UIImage(data: imgData) {
// run the completion block
// always in the main queue, just in case!
dispatch_async(dispatch_get_main_queue(), { () -> Void in
handler(image: img)
})
}
}
}
Async & Sync
doesn’t wait for the closure to finish ,and moves on Always use until you know very well what you are doing
dispatch_async()
won’t return immediately.waits until the closure is done ,could easily get stalled!
dispatch_sync()