In part 1 of my blocks’ adventures I wrote about the basics, block declaration, block definition and general usage. The first thing that you are going to need in iOS development is to use blocks as arguments in Cocoa framework methods, and this is also the easier one. Blocks as methods’ arguments come either to perform an operation on a collection of objects, or to use as a callback after an operation has finished.
So for example a fictional method doSomethingWithBlock which takes block as argument could look like:
[anObject doSomethingWithBlock:^(int x) { NSLog(@"Multiply %i by two", x*2); }];
In this example the doSomethingWithBlock method takes a block as argument. This block takes an integer x which is a parameter that can be accessed inside the implementation of the block. As we said, most of the times you are going to use blocks so let’s say we want to enumerate through an NSArray, if we visit NSArray Class Reference we will find an appropriate method like enumerateObjectsUsingBlock: with definition:
- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block
This is easy to use like:
[theArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){ NSLog(@"The object at index %d is %@",idx,obj); }];
No need for fast enumeration any more. Now with blocks we have simplicity (direct access to every object in the array, its index and the stop variable) and increased speed.
In more complex examples like sortedArrayUsingComparator: with definition
- (NSArray *)sortedArrayUsingComparator:(NSComparator)cmptr
cmptr is a comparator block in which we can define the sort algorithm for the array. Doing that we have the definition of the algorithm at the same place with the function execution, which makes the code easy to read.
NSArray *sortedArray; sortedArray = [drinkDetails sortedArrayUsingComparator:^NSComparisonResult(id a, id b) { NSDate *first = [(Person*)a birthDate]; NSDate *second = [(Person*)b birthDate]; return [first compare:second]; }];
What about blocks and variables?
This is a very interesting story regarding the blocks since we will find a strange behaviour. Don’t forget that blocks are also called closures in other languages.
Blocks have access to non-local variables but they cannot change them. Blocks use them as read-only so for example:
int initialValue = 32; int (^addToInitialValue)(int) = ^(int x) { return initialValue + x; }; NSLog(@"%i", addToInitialValue(10)); // 42 initialValue = 100; NSLog(@"%i", addToInitialValue(10)); // Still 42.
So the block captures initialValue value at the time of its creation.
If we want to modify a variable inside the block, we need to use __block modifier. So using the same example:
__block int initialValue = 32; int (^addToInitialValue)(int) = ^(int x) { return initialValue + x; }; NSLog(@"%i", addToInitialValue(10)); // 42 initialValue = 100; NSLog(@"%i", addToInitialValue(10)); // Now is 110.
Or another example which shows the interaction of blocks with several types of variables:
extern NSInteger CounterGlobal; static NSInteger CounterStatic; { NSInteger localCounter = 42; __block char localCharacter; void (^aBlock)(void) = ^(void) { ++CounterGlobal; ++CounterStatic; CounterGlobal = localCounter; // localCounter fixed at block creation localCharacter = 'a'; // sets localCharacter in enclosing scope }; ++localCounter; // unseen by the block localCharacter = 'b'; aBlock(); // execute the block // localCharacter now 'a' }
In this example, the block aBlock modifies both localCounter and localCharacter. However, outside the block, only the modification to localCharacter is visible, thanks to the __block keyword.
References
1. How to sort an NSMutableArray with custom objects in it?
2. What does the “__block” keyword mean?
3. Objective-C Succinctly: Blocks
4. Blocks Programming Topics
5. Blocks and Variables