How to work with Blocks in iOS 4

Blocks are what help organise your code into independent snippets of code, optimised for re-use and readability. They have been introduced into our UIKit world in iOS 4, along with over 100 Apple APIs that make use of blocks, so it is something that you should start getting used to. What do Blocks look like?

You use the ^ caret operator to signify a block variable, indicative of the beginning of the block.

 

 

int num1 = 7;

int(^aBlock)(int) = ^)int num2) {

   return num1+nunm2;

};

 

 

 

Looking at the code above, we declared the block as a variable, so we can utilise it as a function:

 

NSLog(@"%d", aBlock(49)); //adds 49 to 7 which gives us 56.

 

 

We just saw how to use blocks as variables, but in most cases we use the block literals inline, such as in an argument. The APIs either normally use blocks to perform an operation on a collection of objects, or as a callback after an operation has finished.

 

 

NSComperator compareStringsBlock = ^(id stringA, id stringB) {

NSRange rangeS  = NSMakeRange (0, [stringA length]);

return (stringA compare:stringB options:comparisonOptions range:rangeS locale:currentLocale];

};

NSArray *compareSortArray  = [arrayOfStringDays sortArrayUsingComparator: compareStringsBlock]);

 

 

 

Blocks provide the advantage of creating ad-hoc function body as an expression. Apple Documentation states that:

A block is an anonymous inline collection of code that:

• Has a typed argument list just like a function

• Has an inferred or declared return type

• Can capture state from the lexical scope within which it is defined

• Can optionally modify the state of the lexical scope

• Can share the potential for modification with other blocks defined within the same lexical scope

• Can continue to share and modify state defined within the lexical scope (the stack frame) after the lexical scope (the stack frame) has been destroyed

 

Blocks are small self-contained snippets that encapsulate units of work that may be executed concurrently, over items (line enumeration) or as callbacks.

 

Declaring and Using Blocks

Apple show us in their documentation how we declare a block as a variable, using it as a function

 

int (^oneFrom)(int) = ^(int anInt) {

    return anInt - 1;

};

//We create an inline block literal ^(int anInt)... with the body and the result gets passed to another block OneFrom.

printf("1 from 10 is %d", oneFrom(10));

// Prints "1 from 10 is 9"

 //This block function (distanceTraveled) gets three float arguments, returning float. 

float (^distanceTraveled) (float, float, float) =

                          ^(float startingSpeed, float acceleration, float time) {

    float distance = (startingSpeed * time) + (0.5 * acceleration * time * time);

    return distance;

};

 

 

 

 

You can also pass a block as an argument and instead of declaring them (like we did above) you can simply implement the code inline, where they are required as an argument:

 

NSArray *anArray = [NSArray arrayWithObjects: @"cat", @"dog",nil];

sortFunction(anArray, ^(string *a string *b){

if ( a == @"cat") return TRUE; });

 

 

So we can see we have an inline block snippet take the place of the last argument (has to be the last argument in the list of arguments ).

 

Cocoa provides number of methods that use blocks, so you pass a block as a method argument:

 

NSArray *array = [NSArray arrayWithObjects: @"A", @"B", @"C",  nil];

NSSet *filterSet = [NSSet setWithObjects: @"A", @"Z", @"Q", nil];

BOOL (^test)(id obj, NSUInteger idx, BOOL *stop); //Block declaration returns BOOL, params inc. id and BOOL

//body of block gets the block literal ^(id obj, NSUInteger idx, Bool *stop)... and the body logic 

test = ^ (id obj, NSUInteger idx, BOOL *stop) {

    if (idx < 5) {

        if ([filterSet containsObject: obj]) {

            return YES;

        }

    }

    return NO;

};

 

 

Another example Apple provide :

 

_

_block BOOL found = NO;

NSSet *aSet = [NSSet setWithObjects: @"Alpha", @"Beta", @"Gamma", @"X", nil];

NSString *string = @"gamma";

 //we provide below a way of how to enumerate, using our own compare logic

[aSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {

    if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) {

        *stop = YES;

        found = YES;

    }

}];

 

 

 

 

As you can see, it takes a little while to have it sink in but once you get it, it's quite simple. I suggest looking at Apple's documentation, as well as looking at the referenced APIs to see how they are used. Practice makes perfect.

 

 

Doron KatzApple, iPhone Dev