Objective Cat

A journal of discoveries during iOS and Mac development.

Blocks

14 Sep 2013 by Alexander Schuch

Blocks were introduced way back in iOS 4 and OSX 10.6, but their syntax is sometimes quite confusing and hard to understand. We are going to look at blocks that you might already be familiar with and come up with use cases where you might want to introduce blocks in your own code.

For example, Apple introduced blocks in their Foundation framework, in order to provide a convenient and fast way to loop through arrays and dictionaries.

NSDictionary *numbers = @{@1: @"one", @2: @"two", @3: @"three"};
[numbers enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
  NSLog(@"Number %@", obj);
}];

If you've ever done UIView animations (and you should), the block syntax of animation blocks will look very familiar to you.

[UIView animateWithDuration:0.3f animations:^{
  // your animations
} completion:^(BOOL finished) {
  // something you want to do when it finishes...
}];

Hello, block!

You can think of blocks like functions that perform a specific task and can be assigned to a variable. A block function can later be invoked anywhere in your code. Let's create a little Hello, block! example.

void (^block)();
block = ^{
  NSLog(@"Hello, block!");
};
block(); // prints "Hello, block!"

A block definition consists of four parts.

  1. The return type of the block (void in this example, but could also be NSString*).
  2. The caret ^ marks a pointer to a function (just like the asterisk * for variable pointers).
  3. Right next to the caret is the name of our block (i.e. block).
  4. The parentheses at the end contain parameters that are passed to the block (no parameters in this case, but could e.g. be (NSString*, long)).

The block contents within ^{} can then be assigned to the variable. From now on, a call of block() executes the code that was asssigned to the block variable.


Because blocks can be assigned to a variable, it is also possible to pass them as method parameters. For example, a block that returns the result of an abitrary mathemathical expression of two given variables is passed to the following method in order to change the behaviour of the resulting calculation.

- (int)doMath:(long)a with:(long)b operationBlock:(int (^)(long, long))block {
  int (^operation)(long, long) = [block copy];
  int result = operation(a, b);
  return result * 2;
}

int x = [self doMath:2 with:3 operationBlock:^int(long a, long b) {
  return a * b;
}];
NSLog(@"x = %i", x); // prints x = 12

Of course you could also define a variable for the block (like we've seen before) and pass it to the method, but declaring the block direcly inline increases readybility and is generally the preferred approach. Btw, its always a good idea to copy the passed block before using it - the reference to the passed block might get deallocated at some point. Inline blocks are created on the stack and destroyed as soon as the current scope reaches its end. To store the block on the heap, which will not be destroyed as quickly, we send copy to the passed block.

Confusing block syntax

So far we have only discussed so called inline blocks and passing blocks as method arguments. But there's more and that's where it gets confusing. But awesome too.

The following examples are inspired by twobitlabs' blocks cheat sheet which I have modified in this Github gist.

A block declaration (i.e. its return type and parameters) can be defined using typedef. typedefs make it possible to reuse a block definition for @property and variable definitions as well as creating inline blocks, without the need to write the block syntax again.

@interface CATBlock : NSObject
  typedef NSString* (^FancyBlock)(BOOL);

  // with typedef...
  @property(copy) FancyBlock *configureBlock;
  // ...or without typedef
  // @property(copy) NSString* (^configureBlock)(BOOL);
@end

The configureBlock @property is of type FancyBlock, which was defined in a typedef above, it therefore accepts a block that returns a NSString and accepts a BOOL variable.

@implementation CATBlock
  - (void)runBlocks {
    // assign block to property...
    self.configureBlock = ^ NSString* (BOOL finished) {
      if (finished) {
        return @"awesome";
      }
      return @"sigh";
    };
    NSLog(@"%@", block(YES)); // prints "awesome"

    // ... or create new block with typedef
    // FancyBlock block = ...

    // ... or create new block without typedef
    // NSString* (^anotherBlock)(BOOL) = ^ NSString* (BOOL finished) {
    //  return @"nice!";
    // };
  }
@end

In the implementation of this little example class, we create a block that confirms to the FancyBlock type we defined in the header. (It would be also possible to define the same block variable manually using NSString* (^anotherBlock)(BOOL)) but using typedef is much cleaner.

Some of you have noticed that calling self within a block can lead to a retain circle when the block itself is retained by a class as shown in the example above.

// don't do this!
- (void)someMethod {
  CATBlock *cat = [[CATBlock alloc] init];
  cat.configureBlock = ^ NSString* (BOOL finished) {
    [self processSomething];
    return @"oh no, retain circle!";
  }
}

To avoid this kind of retain circle we must only call a weak instance of self from within the block. The __weak keyword means that the referenced object will not be retained and we are safe to call it from within the block without creating a deathly retain circle.

- (void)someMethod {
  CATBlock *cat = [[CATBlock alloc] init];
  __weak  __typeof(&*self) weakSelf = self;
  cat.configureBlock = ^ NSString* (BOOL finished) {
    [weakSelf processSomething];
    return @"we are good!";
  }
}

If you wanna go crazy, you can even return blocks from methods in order to be able to return some code that can be invoked at a later point.

- (FancyBlock)returnFinishedBlock {
  return ^(BOOL finished) {
    if (finished) {
      return @"awesome";
    }
    return @"sigh";
  };
}
FancyBlock block = [self returnFinishedBlock];
NSLog(@"%@", block(YES)); // prints "awesome"

When should I use blocks?

If you are doing asynchronous network calls in a networking class, parse large data or do some other computation intensive operation in the background, you probably want to track the progress or do something else as soon as your code finishes what its doing. That's really easy with blocks: Just call your method with an additonal completion and progress block and invoke the blocks when you progress or finsh the asynchronous operation.

- (void)downloadPhotos:(NSArray *)photoURLs
              progress:(void (^)(float percent))progress
            completion:(void (^)(BOOL success))completion {
  int count = [photoURLs count];
  for (int i = 0; i < count; i++) {
    // download photo...
    progress(i/(float)count);
  }
  completion(YES);
}

Another use case are abstract implementations for UITableViewDataSource and other protocols as shown in objc.io issue #1. A block is used to configure a UITableViewCell's labels, but the potential of blocks is endless here.

TableViewCellConfigureBlock block = ^(PhotoCell *cell, Photo *photo) {
  [cell configureForPhoto:photo];
};

In general, blocks are often used in order to let users of frameworks execute blocks of code before or after a specific operation or to run a user-defined block of code from different parts of a class.

Conclusion

Blocks can be used in many different ways, and provide a powerful way to execute blocks of code whenever and whereever its needed.

Go, use blocks in your next project and let me know with the use cases you came up with on Twitter.

Further reading

Follow the Objective Cat

If you enjoyed this article, follow the Objective Cat and subscribe to the RSS feed.

© 2013-2014 — Alexander Schuch