Using Blocks

Invoking a Block

If you declare a block as a variable, you can use it as you would a function, as shown in these two examples:

 int (^oneFrom)(int) = ^(int anInt) { return anInt - 1; }; printf("1 from 10 is %d", oneFrom(10)); // Prints "1 from 10 is 9" float (^distanceTraveled)(float, float, float) = ^(float startingSpeed, float acceleration, float time) { float distance = (startingSpeed * time) + (0.5 * acceleration * time * time); return distance; }; float howFar = distanceTraveled(0.0, 9.8, 1.0); // howFar = 4.9

Frequently, however, you pass a block as the argument to a function or a method. In these cases, you usually create a block “inline”.

Using a Block as a Function Argument

You can pass a block as a function argument just as you would any other argument. In many cases, however, you don’t need to declare blocks; instead you simply implement them inline where they’re required as an argument. The following example uses the qsort_b function. qsort_b is similar to the standard qsort_r function, but takes a block as its final argument.

 char *myCharacters = { "TomJohn", "George", "Charles Condomine" }; qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) { char *left = *(char **)l; char *right = *(char **)r; return strncmp(left, right, 1); }); // Block implementation ends at "}" // myCharacters is now { "Charles Condomine", "George", "TomJohn" }

Notice that the block is contained within the function’s argument list.

The next example shows how to use a block with the dispatch_apply function. dispatch_apply is declared as follows:

 void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));

The function submits a block to a dispatch queue for multiple invocations. It takes three arguments; the first specifies the number of iterations to perform; the second specifies a queue to which the block is submitted; and the third is the block itself, which in turn takes a single argument—the current index of the iteration.

You can use dispatch_apply trivially just to print out the iteration index, as shown:

 #include size_t count = 10; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply(count, queue, ^(size_t i) { printf("%u\n", i); });

Using a Block as a Method Argument

Cocoa provides a number of methods that use blocks. You pass a block as a method argument just as you would any other argument.

The following example determines the indexes of any of the first five elements in an array that appear in a given filter set.

 NSArray *array = @[@"A", @"B", @"C", @"A", @"B", @"Z", @"G", @"are", @"Q"]; NSSet *filterSet = [NSSet setWithObjects: @"A", @"Z", @"Q", nil]; BOOL (^test)(id obj, NSUInteger idx, BOOL *stop); test = ^(id obj, NSUInteger idx, BOOL *stop) { if (idx < 5) { if ([filterSet containsObject: obj]) { return YES; } } return NO; }; NSIndexSet *indexes = [array indexesOfObjectsPassingTest:test]; NSLog(@"indexes: %@", indexes); /* Output: indexes: [number of indexes: 2 (in 2 ranges), indexes: (0 3)] */

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. Notice that found is also declared as a __block variable, and that the block is defined inline:

 __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

Copying Blocks

Typically, you shouldn’t need to copy (or retain) a block. You only need to make a copy when you expect the block to be used after destruction of the scope within which it was declared. Copying moves a block to the heap.

You can copy and release blocks using C functions:

 Block_copy(); Block_release();

To avoid a memory leak, you must always balance a Block_copy() with Block_release().

Patterns to Avoid

A block literal (that is, ^{ ... }) is the address of a stack-local data structure that represents the block. The scope of the stack-local data structure is therefore the enclosing compound statement, so you should avoid the patterns shown in the following examples:

 void dontDoThis() { void (^blockArray)(void);  // an array of 3 block references for (int i = 0; i < 3; ++i) { blockArray[i] = ^{ printf("hello, %d\n", i); }; // WRONG: The block literal scope is the "for" loop. } } void dontDoThisEither() { void (^block)(void); int i = random(): if (i > 1000) { block = ^{ printf("got i at: %d\n", i); }; // WRONG: The block literal scope is the "then" clause. } // ... }

Debugging

You can set breakpoints and single step into blocks. You can invoke a block from within a GDB session using invoke-block, as illustrated in this example:

 \$ invoke-block myBlock 10 20

If you want to pass in a C string, you must quote it. For example, to pass this string into the doSomethingWithString block, you would write the following:

 \$ invoke-block doSomethingWithString "\"this string\""