Closure and lambda in Objective-C
 

Closure and anonymous functions in Objective-C


Author: Jean-David Gadina
Copyright: © 2024 Jean-David Gadina - www.xs-labs.com - All Rights Reserved
License: This article is published under the terms of the FreeBSD Documentation License


Table of contents

  1. About
    1. Anonymous functions
    2. Closure
  2. Objective-C implementation
    1. Passing a block as an argument
    2. Closure
    3. Memory management
    4. Examples

1. About

Many scripting languages allows the use of «lambda» or «anonymous functions». That concept is often associated with the «closure» concept.
Such concepts are well known in JavaScript, ActionScript or PHP since version 5.3.
The Objective-C language offers an implementation of both concepts, called «blocks».
Blocks are available since Mac OS X 10.6, thanks to the use of Clang.

Anonymous functions

As it name implies, an anonymous function is a function without a name, nor identifier. It only has its content (body), and can be stored in a variable, for a later use, or to be passed as an argument to another function.

This concept is often used in scripting langauges for callbacks.

In JavaScript, for instance, let's take a standard function called «foo», taking a callback as parameter, and executing it:

function foo( callback )
{
callback();
}

It is possible to define another standard function, and pass it as argument of the first function:

function bar()
{
alert( 'hello, world' );
}

foo( bar );

The problem is that we are declaring a «bar» function in the global scope. So we risk to override another function having the same name.

The JavaScript language allows us to declare the callback function at call time:

foo
{
function()
{
alert( 'hello, world' );
}
);

Here, the callback has no identifier. It won't exist in the global scope, so it can't conflict with another existing function.

We can also define the callback as a variable. It still won't exist in the global scope, but it will be possible to re-use it through the variable:

myCallback = function()
{
alert( 'hello, world' );
};

foo( myCallback );

Closure

The closure concept consist of the possibility for a function to access the variables available in its declaration context, even if it's not the same as it's execution context.

In JavaScript again, let's see the following code:

function foo( callback )
{
alert( callback() );
}

function bar()
{
var str = 'hello, world';

foo
(
function()
{
return str;
}
);
}

bar();

The callback, passed to the «foo» function from the execution context of the «bar» function, returns a variable named «str».
But this variable, declared in the «bar» function's context, is local. It means it exists only from inside that context.
As the callback is executed from a different context than the one containing the variable declaration, we might think the code does not display anything.
But here comes the closure concept.
Even if its execution context is different, a function keeps an access to the variables avaialable in its declaration context.

So the callback will have access to the «str» variable, even if it's called from the «foo» function, which does not have access to the variable.

Objective-C implementation

That kind of concept is available in Objective-C, with some differences, as Objective-C is a strongly typed compiled languaged, built on top of the C language, so very different of an interpreted scripting language.

Note that blocks are also available in pure C, or C++ (and of course also in Objective-C++).

As a standard C function, the declaration of a block (anonymous function) must be preceded by the declaration of its prototype.

The syntax of a block is a bit tricky at first sight, but as function pointers, we get used to it with some time.
Here's a block prototype:

NSString * ( ^ myBlock )( int );

We are declaring here the prototype of a block («^»), that will be named «myBlock», taking as unique parameter an «int», et returning a pointer on a «NSString» object.

Now we can declare the block itself:

myBlock = ^( int number )
{
return [ NSString stringWithFormat: @"Passed number: %i", number ];
};

We assign to the «myBlock» variable a function's body, taking an integer as the «number» argument. This function returns a «NSString» object, in which the integer will be displayed.

Notice: do not forget the semicolon at the end of the block declaration!

If it can be ommited in scripting langauges, it's absolutely necessary for compiled languages like Objective-C.
If it's ommited, the compiler will produce an error, and the final executable won't be generated.

The block can now be used, as a standard function:

myBlock();

Here's the complete source code of an Objective-C program, with the previous example:

#import <Cocoa/Cocoa.h>

int main( void )
{
NSAutoreleasePool * pool;
NSString * ( ^ myBlock )( int );

pool = [ [ NSAutoreleasePool alloc ] init ];
myBlock = ^( int number )
{
return [ NSString stringWithFormat: @"Passed number: %i", number ];
};

NSLog( @"%@", myBlock() );

[ pool release ];

return EXIT_SUCCESS;
}

Such a program can be compiled with the following command (Terminal):

gcc -Wall -framework Cocoa -o test test.m

It will generate an executable named «test», from the «test.m» source file.
To launch the executable:

./test

The declaration of a block prototype can be ommited if the block is not assigned to a variable. For instance, if it's passed directly as a parameter.

For instance:

someFunction( ^ NSString * ( void ) { return @"hello, world" } );

Note that in such a case, the return type must be declared. Here, it's a «NSString» object.

Passing a block as a parameter

A block can of course be passed as an argument of a C function.
Here again, the syntax is a bit tricky at first sight:

void logBlock( NSString * ( ^ theBlock )( int ) )
{
NSLog( @"Block returned: %@", theBlock() );
}

Of course, as Objective-C is a strongly typed language, a function taking a block as argument must also declare it's return type and the type of it's arguments, if any.

Same thing for an objective-C method:

- ( void )logBlock: ( NSString * ( ^ )( int ) )theBlock;

Closure

The closure concept is also available in Objective-C, even if its behaviour is of course different than in interpreted languages.

Let's see the following program:

#import <Cocoa/Cocoa.h>

void logBlock( int ( ^ theBlock )( void ) )
{
NSLog( @"Closure var X: %i", theBlock() );
}

int main( void )
{
NSAutoreleasePool * pool;
int ( ^ myBlock )( void );
int x;

pool = [ [ NSAutoreleasePool alloc ] init ];
x = 42;

myBlock = ^( void )
{
return x;
};

logBlock( myBlock );

[ pool release ];

return EXIT_SUCCESS;
}

The «main» function declares an integer, with 42 as value, and a block, returning that variable.
The block is then passed to the «logBlock» function, that will display its return value.

Even in the execution context of the «logBlock» function, the block, declared in the «main» function, still has access to the «x» integer, and is able to return its value.

Note that blocks also have access to global variable, even the static ones, if they are available in the block declaration context.

Here comes a first difference. The variables available in a block by closure are typed as «const». It means their values can't be modified from inside the block.

For instance, let's see what happen when our block tries to increment the value of «x»:

myBlock = ^( void )
{
x++

return x;
};

The compiler will produce an error, as the «x» variable is only available to read from inside the block.

To allow a block to modify a variable, it has to be declared with the «__block» keyword.
The previous code is valid if we declare the «x» variable in the following way:

__block int x;

Memory management

At the C level, a block is a structure, which can be copied or destroyed.
Two C functions are available for that use: «Block_copy()» and «Block_destroy()».
In Objective-C, a block can also receive the «retain», «release» and «copie» messages, as a normal object.

It's extremely important if a block must be stored for a later use (for instance, stored in a class instance variable).

In such a case, the block must be retained, in order to avoid a segmentation fault.

Examples

Blocks can be used in a lot of different contexts, to keep the code simple and to reduce the number of declared functions.

Here's an example:

We are going to add to the «NSArray» class a static method (class method) that will generate an array by filtering the items of another array, by a callback.

For the PHP programmers, it's the same as the «array_filter()» function.

First, we need to declare a category for the «NSArray» class.
A category allows to add methods to existing classes.

@interface NSArray( BlockExample )

+ ( NSArray * )arrayByFilteringArray: ( NSArray * )source withCallback: ( BOOL ( ^ )( id ) )callback;

@end

Here, we are declaring a method returning a «NSArray» object, and taking as parameter another «NSArray» object, and a callback, as a block.

The callback will be executed for each item of the array which is passed as parameter.
It will return a boolean value, in order to know if the current array item must be stored or not in the returned array.
The block takes as unique parameter an object, representing the current array item.

Let's see the implementation of that method:

@implementation NSArray( BlockExample )

+ ( NSArray * )arrayByFilteringArray: ( NSArray * )source withCallback: ( BOOL ( ^ )( id ) )callback
{
NSMutableArray * result;
id element;

result = [ NSMutableArray arrayWithCapacity: [ source count ] ];

for( element in source ) {

if( callback( element ) == YES ) {

[ result addObject: element ];
}
}

return result;
}

@end

First, we create an array with a dynamic size («NSMutableArray»). It's initial capacity is the same as the number of items of the source array.

Then, we iterate through each item of the source array, and we add the current item if the result of the callback is the boolean value «YES».

Here's a complete example of a program using such a method.
We are using the callback to create an array that contains only the items of type «NSString» from the source array:

#import <Cocoa/Cocoa.h>

@interface NSArray( BlockExample )

+ ( NSArray * )arrayByFilteringArray: ( NSArray * )source withCallback: ( BOOL ( ^ )( id ) )callback;

@end

@implementation NSArray( BlockExample )

+ ( NSArray * )arrayByFilteringArray: ( NSArray * )source withCallback: ( BOOL ( ^ )( id ) )callback
{
NSMutableArray * result;
id element;

result = [ NSMutableArray arrayWithCapacity: [ source count ] ];

for( element in source ) {

if( callback( element ) == YES ) {

[ result addObject: element ];
}
}

return result;
}

@end

int main( void )
{
NSAutoreleasePool * pool;
NSArray * array1;
NSArray * array2;

pool = [ [ NSAutoreleasePool alloc ] init ];
array1 = [ NSArray arrayWithObjects: @"hello, world", [ NSDate date ], @"hello, universe", nil ];
array2 = [ NSArray
arrayByFilteringArray: array1
withCallback: ^ BOOL ( id element )
{
return [ element isKindOfClass: [ NSString class ] ];
}
];

NSLog( @"%@", array2 );

[ pool release ];

return EXIT_SUCCESS;
}