Constructing Block Objects and Their Syntax

Block objects can either be inline or coded as independent blocks of code. Let’s start with the latter type. Suppose you have a method in Objective-C that accepts two integer values of type NSInteger and returns the difference of the two values, by subtracting one from the other, as an NSInteger:

- (NSInteger) subtract:(NSInteger)paramValue
                  from:(NSInteger)paramFrom{

  return paramFrom - paramValue;

}

That was very simple, wasn’t it? Now let’s translate this Objective-C code to a pure C function that provides the same functionality to get one step closer to learning the syntax of block objects:

NSInteger subtract(NSInteger paramValue, NSInteger paramFrom){

  return paramFrom - paramValue;

}

You can see that the C function is quite different in syntax from its Objective-C counterpart. Now let’s have a look at how we could code the same function as a block object:

NSInteger (^subtract)(NSInteger, NSInteger) =
  ^(NSInteger paramValue, NSInteger paramFrom){

  return paramFrom - paramValue;

};

Before I go into details about the syntax of block objects, let me show you a few more examples. Suppose we have a function in C that takes a parameter of type NSUInteger (an unsigned integer) and returns it as a string of type NSString. Here is how we implement this in C:

NSString* intToString (NSUInteger paramInteger){

  return [NSString stringWithFormat:@"%lu",
          (unsigned long)paramInteger];

}

Note

To learn about formatting strings with system-independent format specifiers in Objective-C, please refer to String Programming Guide, iOS Developer Library on Apple’s website.

The block object equivalent of this C function is shown in Example 1-1.

Example 1-1. Example block object defined as function

NSString* (^intToString)(NSUInteger) = ^(NSUInteger paramInteger){
  NSString *result = [NSString stringWithFormat:@"%lu",
                      (unsigned long)paramInteger];

  return result;
};

The simplest form of an independent block object would be a block object that returns void and does not take any parameters in:

void (^simpleBlock)(void) = ^{
  /* Implement the block object here */
};

Block objects can be invoked in the exact same way as C functions. If they have any parameters, you pass the parameters to them like a C function and any return value can be retrieved exactly as you would retrieve a C function’s return value. Here is an example:

NSString* (^intToString)(NSUInteger) = ^(NSUInteger paramInteger){
  NSString *result = [NSString stringWithFormat:@"%lu",
                      (unsigned long)paramInteger];
  return result;
};

- (void) callIntToString{

  NSString *string = intToString(10);
  NSLog(@"string = %@", string);

}

The callIntToString Objective-C method is calling the intToString block object by passing the value 10 as the only parameter to this block object and placing the return value of this block object in the string local variable.

Now that we know how to write block objects as independent blocks of code, let’s have a look at passing block objects as parameters to Objective-C methods. We will have to think a bit abstractly to understand the goal of the following example.

Suppose we have an Objective-C method that accepts an integer and performs some kind of transformation on it, which may change depending on what else is happening in our program. We know that we’ll have an integer as input and a string as output, but we’ll leave the exact transformation up to a block object that can be different each time our method runs. This method, therefore, will accept as parameters both the integer to be transformed and the block that will transform it.

For our block object, we’ll use the same intToString block object that we implemented earlier in Example 1-1. Now we need an Objective-C method that will accept an unsigned integer parameter and a block object as its parameter. The unsigned integer parameter is easy, but how do we tell our method that it has to accept a block object of the same type as the intToString block object? First we typedef the signature of the intToString block object, which tells the compiler what parameters our block object should accept:

typedef NSString* (^IntToStringConverter)(NSUInteger paramInteger);

This typedef just tells the compiler that block objects that accept an integer parameter and return a string can simply be represented by an identifier named IntToStringConverter. Now let’s go ahead and write our Objective-C method that accepts both an integer and a block object of type IntToStringConverter:

- (NSString *) convertIntToString:(NSUInteger)paramInteger
                 usingBlockObject:(IntToStringConverter)paramBlockObject{

  return paramBlockObject(paramInteger);

}

All we have to do now is call the convertIntToString: method with our block object of choice (Example 1-2).

Example 1-2. Calling the block object in another method

- (void) doTheConversion{

  NSString *result = [self convertIntToString:123
                             usingBlockObject:intToString];

  NSLog(@"result = %@", result);

}

Now that we know something about independent block objects, let’s turn to inline block objects. In the doTheConversion method we just saw, we passed the intToString block object as the parameter to the convertIntToString:usingBlockObject: method. What if we didn’t have a block object ready to be passed to this method? Well, that wouldn’t be a problem. As mentioned before, block objects are first-class functions and can be constructed at runtime. Let’s have a look at an alternative implementation of the doTheConversion method (Example 1-3).

Example 1-3. Example block object defined as function

- (void) doTheConversion{

  IntToStringConverter inlineConverter = ^(NSUInteger paramInteger){
    NSString *result = [NSString stringWithFormat:@"%lu",
                        (unsigned long)paramInteger];
    return result;
  };

  NSString *result = [self convertIntToString:123
                             usingBlockObject:inlineConverter];

  NSLog(@"result = %@", result);

}

Compare Example 1-3 to the earlier Example 1-1. I have removed the initial code that provided the block object’s signature, which consisted of a name and argument, (^intToString)(NSUInteger). I left all the rest of the block object intact. It is now an anonymous object. But this doesn’t mean I have no way to refer to the block object. I assign it using an equal sign to a type and a name: IntToStringConverter inlineConverter. Now I can use the data type to enforce proper use in methods, and use the name to actually pass the block object.

In addition to constructing block objects inline as just shown, we can construct a block object while passing it as a parameter:

- (void) doTheConversion{

  NSString *result =
  [self convertIntToString:123
          usingBlockObject:^NSString *(NSUInteger paramInteger) {

    NSString *result = [NSString stringWithFormat:@"%lu",
                        (unsigned long)paramInteger];
    return result;

  }];

  NSLog(@"result = %@", result);

}

Compare this example with Example 1-2. Both methods use a block object through the usingBlockObject syntax. But whereas the earlier version referred to a previously declared block object by name (intToString), this one simply creates a block object on the fly. In this code, we constructed an inline block object that gets passed to the convertIntToString:usingBlockObject: method as the second parameter.

I believe that at this point you know enough about block objects to be able to move to more interesting details, which we’ll begin with in the following section.