Block initialization for testability and reuse

BlocksSince Apple introduced block support in iOS 4, more and more APIs are moving from delegation to block callbacks. While block callbacks can be declared inline, in most cases you should initialize your block callback in a method that returns the block. This keeps the code that calls an external API succinct, allows you to reuse the block in different contexts, and makes it easier to unit test the code in the block.

Let’s see what this looks like in practice.

Blocks as Return Values

One common API that uses blocks is UIView animation:

+(void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations

Using a method to create the animation block looks like this:

-(IBAction)someButtonPressed:(id)sender {
    [self saveSomeData];
    [UIView animateWithDuration:.5f animations:[self slideOffStageRight]];

}

-(void (^)(void))slideOffStageRight {
    return ^ {
        self.screen.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:.5f];  
        self.someView.frame = CGRectMake(UIInterfaceOrientationIsPortrait(self.interfaceOrientation) ? 320 : 480,
                                         Y(self.someView), WIDTH(self.someView), HEIGHT(self.someView));
        }
    };
}

There are a few nice things about this approach:

  • It’s succinct. The code path you care about makes the call to fire the animation and continues. In this way, it’s very similar to delegation, where the code that handles delegate callbacks isn’t inline when you invoke the API.
  • It’s idiomatic. It reads like a sentence–“animate off stage right”. What I care about when I look at someButtonPressed is the style of animation in use, not how it’s implemented. It’s also clear what the author intended, which is helpful when the result isn’t what you’d expect.
  • It’s reusable. If there are multiple places that could invoke this animation, you don’t repeat the block definition. And if there’s a bug in the animation block, you can fix it once.
  • It’s testable. We’re big proponents of test-driven development. With this approach, you can invoke the animation block directly in a spec:
it(@"should slide off to the right", ^{
    [controller slideOffStageRight]();
    expect(controller.screen.backgroundColor).toEqual([[UIColor blackColor] colorWithAlphaComponent:.5f]);
    expect(X(controller.someView)).toEqual(320);
});

Inline Blocks

The approach you’re probably most familiar with is initializing the animation block inline. Let’s see how that looks:

-(IBAction)someButtonPressed:(id)sender {
    [self saveSomeData];
    [UIView animateWithDuration:.5f animations:^{
        self.screen.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:.5f];  
        self.someView.frame = CGRectMake(UIInterfaceOrientationIsPortrait(self.interfaceOrientation) ? 320 : 480,
                                         Y(self.someView), WIDTH(self.someView), HEIGHT(self.someView));
    }];
}

It’s shorter, but it has a few drawbacks:

  • It breaks the code’s flow. Reading the containing method, you suddenly hit this chunk of code that clearly animates something. But to understand how it animates, you have to build a mental model of the views and imagine the changes. Or you can ignore it. But you can’t just glance at it and think “Ok, it slides off to the right here.”
  • It’s not reusable. If you need the same animation elsewhere, you’re likely to copy/paste the code. Then, when you change it, you’re likely to miss the other instance of the same animation.
  • It’s hard to test. The only way to test it is to invoke the method surrounding method, block for the duration of the animation, and assert the resulting state. If the block defines a callback for some asynchronous API, it gets even more complicated.

Blocks as Properties

One pattern I see a lot is creating a block during initialization and storing it in a property. Here’s what this looks like with our example:

@property(copy)void(^slideOffStageRight)();

@synthesize slideOffStageRight;

-(id)init {
    self = [super init];
    if (self) {
        __weak Foo *bself = self;
        self.slideOffStageRight = ^{
            bself.screen.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:.5f];  
            bself.someView.frame = CGRectMake(UIInterfaceOrientationIsPortrait(self.interfaceOrientation) ? 320 : 480,
                                             Y(self.someView), WIDTH(self.someView), HEIGHT(self.someView));
        };
    }
    return self;
}

-(IBAction)someButtonPressed:(id)sender {
    [self saveSomeData];
    [UIView animateWithDuration:.5f animations:self.slideOffStageRight];

}

The property approach makes the block reusable, but it too has a couple of drawbacks:

  • You have to beware of retain cycles. Note the bself variable. This is necessary because blocks automatically retain object variables they reference. If the block were to reference self directly, the block would increase self’s retain count when it’s created, creating a retain cycle. As a result, all instances of this object would leak unless the caller explicitly un-sets the slideOffStageRight property. But callers shouldn’t have to worry about this object’s internal state.
  • It’s inflexible. What if you want to apply this animation to different views? If you create a block at initialization and store it in a property, it is what it is. With the method return approach, you can just modify the method to take the view to animate as an argument:

Summary

With advantages both over block properties and inline block definitions, I find that initializing reusable blocks in methods keeps me sane. The code is succinct and flexible, memory leaks are reduced, and the thorough test coverage keeps me confident that the code does what I expect it to. I encourage you to try this approach out and let me know what you think.

8 Comments

  1. George Cook
    October 19, 2012

    nice write up. I think it’s actually an improvement to what I frequently do (which is delegate the block to a method).

    I’ll give it a roadtest and let you knowhow I get on.

    Reply
  2. Edward
    October 23, 2012

    Do you lose the ability to access local variables that would be in scope if you declared the block inline? If so, do you consider this a disadvantage for blocks as a return value?

    Reply
    • Christopher Pickslay
      October 23, 2012

      Great question, Edward. You can easily define the method that returns the block to take an argument, and pass the local variable(s) into it. For example, say we wanted to slide any view off to the right. You could define the method as:

      -(IBAction)someButtonPressed:(id)sender {
          [self saveSomeData];
          UIView *someView = [self getTheViewThatCorrespondsToButton:sender];
          [UIView animateWithDuration:.5f animations:[self slideOffStageRight:someView]];

      }

      -(void (^)(void))slideOffStageRight:(UIView *)someView {
          return ^ {
              self.screen.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:.5f];  
              someView.frame = CGRectMake(UIInterfaceOrientationIsPortrait(self.interfaceOrientation) ? 320 : 480,
                                          Y(someView), WIDTH(someView), HEIGHT(someView));
              }
          };
      }
      Reply
      • Edward
        October 24, 2012

        Yup. That is definitely a solution that I considered to get around the issue. The only problem is that sometimes I’m working with a 3rd party library API that defines the block a method takes, and I wouldn’t have control over that. What would you do in that situation? Wrap their API with your own to take the arguments you need?

        Reply
        • Christopher Pickslay
          October 24, 2012

          I don’t think that’s any different. The third party API expects a block with a certain signature. But your method that returns that block can define its own signature, in terms of the arguments it accepts. So your call to the third party API might look something like:

          NSString *someLocalString = @"foo";
          [ThirdPartyAPI doSomethingWithCallback:[self callbackForString:someLocalString]];
          Reply
          • Edward
            October 28, 2012

            ah yup. I see what you mean now. awesome

  3. James Frost
    March 16, 2013

    Great post! Your approach helps to counteract one of my main issues with code that uses a lot of blocks – namely that methods get long and hard to read.

    One quick question though – what is it about the first example that means there’s no retain cycle when using ‘self’ in the block?

    Reply
    • Christopher Pickslay
      March 18, 2013

      Thanks James. In the first example, the block is only created in response to some user interaction, and disposed of at the end of the animation. So self is only retained while the block is executed, which should be while UIKit sets up the animation.

      By contrast, when you store a block in a property, the block is created whenever you initialize the property, and not disposed of until the instance that references it is deallocated. If you inadvertently leave a strong reference to self in the block, the instance that owns it will never be deallocated, because the block prevents its retain count from going to zero. Before weak references became available in iOS 5, I saw lots of ugly code where people tried to determine that the object’s lifecycle was over, and set the block property to nil so the object could be deallocated.

      Reply

Leave a Reply to Christopher Pickslay

Cancel Reply