Block object

Block objects are a C-level syntactic and runtime feature that allow you to compose function expressions that can be passed as arguments, optionally stored, and used by multiple threads. The function expression can reference and can preserve access to local variables. In other languages and environments, a block object is sometimes also called a closure or a lambda. You use a block when you want to create units of work (that is, code segments) that can be passed around as though they are values. Blocks offer more flexible programming and more power. You might use them, for example, to write callbacks or to perform an operation on all the items in a collection.

Declaring a Block

In many situations, you use blocks inline so you don’t need to declare them. The declaration syntax, however, is similar to the standard syntax for function pointers, except that you use a caret (^) instead of an asterisk pointer (*). For example, the following declares a variable aBlock that references a block that requires three parameters and returns a float value:

float (^aBlock)(const int*, int, float);

Creating a Block

You use the caret (^) operator to indicate the beginning, and a semicolon to indicate the end of a block expression. The following example declares a simple block and assigns it to a previously declared block variable (oneFrom):

int (^oneFrom)(int);
 
oneFrom = ^(int anInt) {
    return anInt - 1;
};

The closing semicolon is required as a standard C end-of-line marker.

If you don’t explicitly declare the return value of a block expression, it can be automatically inferred from the contents of the block.

Block-Mutable Variables

You can use the __block storage modifier with variables declared locally to the enclosing lexical scope to denote that such variables should be provided by reference in a block and so are mutable. Any changes are reflected in the enclosing lexical scope, including any other blocks defined within the same enclosing lexical scope.

Using Blocks

If you declare a block as a variable, you can use it as you would a function. The following example prints 9 as output.

printf("%d\n", oneFrom(10));

Typically, however, you pass a block as the argument to a function or a method. In these cases, you often create a block inline.

The following example determines whether an NSSet object contains a word specified by a local variable and sets the value of another local variable (found) to YES (and stops the search) if it does. In this example, found is declared as a __block variable.

__block BOOL found = NO;
NSSet *aSet = [NSSet setWithObjects: @"Alpha", @"Beta", @"Gamma", @"X", nil];
NSString *string = @"gamma";
 
[aSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
    if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) {
        *stop = YES;
        found = YES;
    }
}];
 
// At this point, found == YES

In this example, the block is contained within the method’s argument list. The block also makes use of a stack local variable.

Comparison Operations

One of the more common operations you perform with blocks in a Cocoa environment is comparison of two objects—to sort the contents of an array, for example. The Objective-C runtime defines a block type—NSComparator—to use for these comparisons.

Prerequisite Articles

Related Articles