Introduction to Objective-C for Programmers, part V

Introduction to Objective-C for Programmers, part V

Introduction to Objective-C for Programmers contains the following parts already:

This is the fifth part of the series, today I will describe what blocks are, how to use them and why they are usefull.

Simple Block Definition

To grasp this concept fully it would be nice to have a bit more c-background, especially around function pointers and/or at least to understand the concept of closures.
Blocks in Objective-C have some peculiar syntax (strange syntax is not a surprise here ☺), and they need a bit of time to get used to.
Block in its simplest form look like this:

^{
}

Of course we can assign it to a variable:

void (^myBlock)() = ^{

};

the ‘myBlock’ is a block type variable which takes no parameters () and returns void.

Parameters and return values

Blocks, similar to standard functions, can take parameters and return values, the block example below will take two parameters and return the result as integer:

  int (^multiply)(int a, int b) = ^(int a, int b){
  return a * b;
};

You can actually use that block like a standard function:

  int result = multiply(2,2); //will assign value 4 to the result variable

Capturing Values and Sharing Storage

Blocks can capture values from the enclosing scope, best explained in a simple example taken from the iOS developer library:

  int anInteger = 42;

  void (^testBlock)(void) = ^{
    NSLog(@"Integer is: %i", anInteger);
  };

  anInteger = 84;

  testBlock();

will print ‘Integer is: 42’
As you can see the value of the captured variable hasn’t changed while the block was executing, it stayed with the value it had while it was captured.
If you want actually to change the value of the captured variable you would need to use a storage type modifier ___block

Example:

__block int anInteger = 42;

void (^testBlock)(void) = ^{
  NSLog(@"Integer is: %i", anInteger);
};

anInteger = 84;

testBlock();

Which prints Integer is: 84

Last thing worth mentioning about block capture capabilities is about capturing ‘self’. If you capture ‘self’ in the following way:

  self.block = ^{
  [self doSomething]; // capturing a strong reference to self
  // creates a strong reference cycle
};

You will create a strong reference cycle, to avoid that problem you would need to use another modifier called ‘___week’, eg:

- (void)configureBlock {
  SomeObjectYouAreIn * __weak weakSelf = self;
  self.block = ^{
  [weakSelf doSomething]; // capture the weak reference
  // to avoid the reference cycle
}
}

Stack or Heap

When you create a new block, the block is automatically created on the stack so you should assume that it will have a lifespan of the scope it was created in. If you want your blocks to live longer you need to copy them over to the heap space using ‘copy’ method, eg:

void (^block)() = [^{
  NSLog(@"My Block");
} copy];

If you have a block which don’t capture any enclosing scope variables you can create the block as global.

For convenience when using block you can use typedefs and standard ARC managed properties:

typedef void(^MyCustomBlock)(void);
  @interface MyClass : NSObject
  @property (nonatomic, copy) MyCustomBlock customBlock;
@end

@implementation MyClass
@end

//use it as usual
MyClass * c = [[MyClass alloc] init];

c.customBlock = ^{ NSLog(@"hello.....");

c.customBlock();

What is it for

I guess the main purpose of using blocks is to have everything in one place, lets say that you want to execute something asynchronously than, without blocks, you would need to get notified about the result of some async operation when it finishes, with blocks you can define that in the same place where you start the operation, this way your code will be more readable

Example with UIView animations

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(animationDidStop:context:)];
.... set up animation ....
[UIView commitAnimations];

where with blocks you can simple do that in a single place

[UIView animateWithDuration:1.0 animations:^{
  // set up animation
} completion:^{
  // this will be executed on completion
}];

another example with asynchronous requests and anonymous blocks

[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *rsp, NSData *d, NSError *e) {
  // process request here
}];

Another cool thing about blocks is that you can use them to apply some logic to the collection elements while enumerating over them, for that purpose special methods exists which allow you to do just that, eg:

NSArray *array = ...
[array enumerateObjectsUsingBlock:^ (id obj, NSUInteger idx, BOOL *stop) {
  NSLog(@"Object at index %lu is %@", idx, obj);
}];

Example, sorting with blocks:

NSArray *sortedArray; sortedArray = [drinkDetails sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
  NSDate *first = [(Person*)a birthDate];
  NSDate *second = [(Person*)b birthDate];
  return [first compare:second];
}];

Filtering with blocks:

NSArray *filteredArray = [array filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *bindings) {
return [object shouldIKeepYou]; // Return YES for each object you want in filteredArray.
}]];

or:

yourArray = [yourArray objectsAtIndexes:[yourArray indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
  return [self testFunc:obj];
}]];

You can read more about block usage example here: http://parmanoir.com/8_ways_to_use_Blocks_in_Snow_Leopard

Introduction to Objective-C for Programmers contains the following parts already:

Leave a Reply