Closures and Closure Expressions - ADDITIONAL TOPICS - Understanding Swift Programming: Swift 2 (2015)

Understanding Swift Programming: Swift 2 (2015)

PART 3: ADDITIONAL TOPICS

18. Closures and Closure Expressions

A closure is a general term that can refer to either a function or a closure expression. Both include chunks of code that perform a specific task. Both can, if desired, (but are not required to) accept values as input parameters and return a value that is the result of the processing the code has done.

Closures, in the right context, will capture, or close upon, the names and values of variables and constants that are in their scope that the code in them refers to and package these variables and constants together with the code of the closure for use in later execution. It is this capability that they are named for. Closures in Swift are sometimes known as blocks and they are the same idea as that of a block in Objective-C. A Swift closure, either a function or a closure expression, can be used in any Objective-C API that will take a block.

What's the difference between a function and a closure expression? Functions have names and a specific, and somewhat rigid, syntax. Closure expressions do not have names and are sometimes known as anonymous functions because of this. Closure expressions have their own syntax, different from a function, with that syntax being very flexible and allowing many forms.

In practice, functions often have substantial amounts of code and a big motivation for creating them is so that the same function can be called in different situations, thus making programming efficient by reusing code. Closure expressions usually have relatively small amounts of code and are typically used only once.

Closure expressions are often used inline, meaning that the closure expression allows code to be an argument in a call to a method (often a call to an API). This often makes code more readable and direct—the code is often more easily found because it is close to the API call rather than being buried in a function somewhere else that is called.

There is often confusion about terminology. Although both functions and closure expressions are closures, when developers use the term closure they more often than not actually mean closure expression. Some developers never use the term closure expression but call them anonymous functions instead.

Closures, whether functions or closure expressions, are objects and are reference types, meaning that they are stored as part of the heap memory, and it is possible for closures to have reference cycles.

The Syntax of A Closure Expression

The figure above shows the syntax of a closure expression. Nearly every part of it can be optional. This diagram shows the syntax as it is normally used. In some narrow circumstances, even the braces are not required and a conditional expression can be used instead of a statement.

We can compare the full syntax for a Swift function and a Swift closure for the same code, as follows:

func addTwoNumbers (a: Int, b: Int) -> Int { // Swift function

var c = a + b

return c

}

This function, which adds together two integers, is called as follows:

var d = addTwoNumbers(2, b: 5)

print(d) // Prints: 7

The same chunk of code that goes in a function can also go in a closure expression:

var e = { (a: Int, b: Int) -> Int in

var c = a + b

return c

}

In this example we have assigned this closure expression to the variable e. We can then execute the variable, which will execute the closure expression:

var d = e(2, 5)

print(d) // Prints: 7

This does exactly the same thing as the line above that calls the addTwoNumbers function:

var d = addTwoNumbers(2, b: 5)

print(d) // Prints: 7

If we look closely at the syntax for this closure expression:

var e = { (a: Int, b:Int) -> Int in

var c = a + b

return c

}

we find that it has almost everything that is shown in the diagram for closure expression syntax: a leading brace, input parameters, a return arrow, a return type, the keyword in, statements, and a trailing brace.

One thing that it does not have is a capture list. A capture list is a list of references (variables and constants) that might cause what is known as a memory reference cycle. The list contains a tag for each reference that indicates how to avoid the reference cycle. Reference cycles can cause memory leaks, in which memory that is no longer needed cannot be properly recycled. See Chapter 19 on "Memory Management" for details on reference cycles and how capture lists can be used to avoid them with closure expressions.

Using a Closure Expression in an Input Parameter of a Function

Perhaps the most common use of a closure expression is using it as an argument in a call to a function. Very often this is a call to an API that is part of the Cocoa Touch or other library in iOS. Here’s an example:

The iOS system (and Mac too) has a threading system known as Grand Central Dispatch. In an iOS app, you often want to execute some code on a background thread rather than the main thread of execution so as to not interfere with the flow of processing for the user interface, which runs on the main thread. You don't want the UI to freeze or stutter. Grand Central Dispatch makes it easy to run code on a background thread. You simply put that code in one of several queues (depending upon the priority level that you want the code to run with) and the Grand Central Dispatch system will take care of running it on the appropriate thread or threads.

The code is:

let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT

let queue = dispatch_get_global_queue(priority, 0)

dispatch_async(queue, { ()->() in

print("Hello from the default priority background queue")

})

The code here first defines a priority level, then gets a reference to a queue of the desired type (We want a concurrent queue rather than a serial one) and desired priority (We want the default level, a medium level of priority).

The dispatch_async API call has two parameters. The first is a reference to the queue that was just created. The second expects a closure with the code to be executed. This can be either a function or a closure expression.

Here we provide it as a closure expression, which has the conventional syntax, indicating that there are no input parameters and no return values—just some code, a single print statement.

Defining a Function to Accept a Closure

We have to take some care in defining a function so that it will accept a closure. We cannot take too literally the idea that a closure is merely a "chunk of code", as Apple likes to say. It is a little more than that.

For example, we can rewrite the addTwoNumbers function so that the second input parameter, which normally takes an integer, takes a closure instead.

Here's the function:

func addTwoNumbers (a: Int, b: Int) -> Int {

var c = a + b

return c

}

And how we call it:

var d = addTwoNumbers(2, b: 5)

print(d) // Prints: 7

We might think that the only thing we have to do differently is to call it with a closure instead of an integer:

var d = addTwoNumbers(2, b: { return 5 }) // Compiler error

But no. The problem is that the function is typed to accept an Int as the second parameter, not a closure. We have to do two things. First, we have to change the type of the second parameter of the function. And second, we have to execute the closure to produce the resulting integer. Below, we've set the type of the closure in the input parameter of the function to ()->Int, indicating that it has no input but produces a return value of an Int. And we've changed b to b() in the code inside the function so as to execute the closure:

func addTwoNumbersClosure (a: Int, b: ()->Int) -> Int {

var c = a + b()

return c

}

We can now call it as before:

var d = addTwoNumbersClosure(2, b: { return 5 })

print(d) // Prints: 7

Hands-On Exercises

Go to the following web address with a Macintosh or Windows PC to do the Hands-On Exercises.

For Chapter 18 exercises, go to

understandingswiftprogramming.com/18





All materials on the site are licensed Creative Commons Attribution-Sharealike 3.0 Unported CC BY-SA 3.0 & GNU Free Documentation License (GFDL)

If you are the copyright holder of any material contained on our site and intend to remove it, please contact our site administrator for approval.

© 2016-2026 All site design rights belong to S.Y.A.