Mastering macOS Programming
上QQ阅读APP看书,第一时间看更新

Closures

We don't necessarily have to have previously defined a function in order to pass code to a higher order function. We can pass a closure, a (usually) nameless block of code. A closure may look something like these two examples:

{ print("I am a closure") } 
{ return "I am a closure" }

The first of these is of type () -> Void, which we defined in the preceding Subroutine alias. The second is of type () -> String.

Now let's define a couple of artificially simple higher order functions, that can take these closures as arguments:

func executeSubroutine(f: Subroutine) 
{
f()
}

func executeStringGenerator(f: () ->String )
{
let message = f()
print(message)
}

So, all that these two functions do is call the functions that they have been passed in their argument list.

We call these higher order functions as we would any other function, passing the closure as the single parameter:

executeSubroutine(f: { print("I am a closure") } ) 
executeStringGenerator(f: { return"I am a closure" })

Both these calls will print the message to the screen, of course.

Before we move on to closures that themselves take arguments (which is where their usefulness starts to become apparent), there is an alternative syntax that should be mentioned. The two lines of code above can be written with so-called trailing closures, meaning that when the last argument of a function is a closure, we can write it outside of any brackets, as follows:

executeSubroutine { print("I am a closure") }
executeStringGenerator { return"I am a closure" }

Now, this syntax doesn't look anything like the standard syntax which we know from the C language family. It has its advantages, a discussion of which is beyond the scope of this book. If you are comfortable with it, including when it is nested inside other functions that use this syntax (where things start to get a little unclear sometimes), then go ahead and use it.

We will not use it in this book, for reasons of familiarity, and therefore clarity. But that should not be taken as an implicit rejection of the syntax.

We can also assign a closure to a variable, like this:

let myGreeting = {print("G'day, mate!")} 

Now we can use myGreeting as the parameter in a call to executeSubroutine:

executeSubroutine(f: myGreeting) 

Why wouldn't we just call the print statement directly? Well, we might want to make use of the fact that executeSubroutine doesn't know or care anything about the details of the function that it executes. We could pass it a different function, like this one:

let myOtherGreeting = { 
if loggedIn==true
{
print("G'day, mate!")
}
}

The executeSubroutine is completely unaware of any loggedIn status that may exist, and may or may not need to be checked. The part of the program responsible for checking whether being logged in is important or not can make that decision, and pass the appropriate function to executeSubroutine.

The syntax and use of closures really underscores the objective nature of functions.