Cowboy Tech

Grand_Central_Dispatch

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()