Carpe diem (Felix's blog)

I am a happy developer

C/ObjC Block Syntax Explained

Block is heavily used in objective-c APIs. If you want to write concurrent and responsive interface, you will need blocks and grand central dispatch. Blocks also bring many goods form functional programming to cocoa. It is just awesome.

However, when I first learn blocks I found the syntax confuses me a lot. This is why I wrote this article. Hope this article can help people who have the same problem as I did.

Declare a block variable

Block syntax inherited form C’s function pointers. To declare a block variable, you write:

1
2
3
4
5
6
7
    int (^multiply)(int, int) = ^int(int a, int b) { return a*b; };
  //^^^^^^^^^^^^^^^^^^^^^^^^          ^^^^^^^^^^^  ^^^^^^^^^^^^^^^
  //  declare block variable          block          block body
  //  "multiply"                      argument

    int result = multiply(3, 5); // 15
  // execute a block

It is similar to function pointer:

1
2
3
4
5
6
7
    int mutiply(int a, int b){
        return a*b;
    }

    int (*functionPt)(int, int) = &mutiply;

    functionPt(3, 5); // 15

Block literal syntax shortcut

Block literal can be written in various ways:

1
2
3
4
5
6
7
8
9
10
    int (^myBlock)(int) = ^int(int a) {...};
    int (^myBlock)(int) = ^(int a) {...};   // same

    int (^myBlock)(void) = ^(void) {...};
    int (^myBlock)(void) = ^{...};          // same
    int (^myBlock)() = ^{...};              // same

    void (^myBlock)() = ^{...};             // valid
    (^myBLock)() = ^{...};                  // invalid
    void (^myBlock) = ^{...};               // invalid

Anonymous block

You don’t need a block variable to use a block. A block without a block variable is called anonymous block.

1
2
    // An anonymous block
    ^int (id obj1, id obj2) {...};

Many objective-c methods accepts anonymous block:

1
2
3
4
    NSArray *sortedArray = [unsortedArray sortedArrayUsingComparator:
        ^(id obj1, id obj2){
            ...
        }];

Compare with function pointer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    struct s_data
    {
        int a;
        char* name[10];
        unsigned b;
    };

    NSInteger compareFunction(id obj1, id obj2, void* context){
        struct s_data *data = (struct s_data *) context;
        // do things
    };

    sturct s_data *my_data = (struct s_data *)malloc(sizeof(s_data));
    s_data.a = -3;
    s_data.name = "abc";
    s_data.b = 5;
    void *context = my_data;
    NSArray *sortedArray = [unsortedArray sortedArrayUsingFunction: compareFunction
                                                           context:context];

What does above mean? Well, if you want to pass a callback function to elsewhere, sometimes you might also need to pass data. To do that, you first pack your data into a struct, and use a void pointer points to it. Then you pass the callback function and the void pointer to the function. Finally you dereference the void pointer back to the struct.

With block, all variables in it are captured. You no longer need to do that type casting hack to pass data.

1
2
3
4
5
6
7
    NSNumber *num = @3;

    NSArray *sortedArray = [unsortedArray sortedArrayUsingComparator:
        ^(id obj1, id obj2){
            return [num compare: obj1];
            // num is retained inside the block
        }];

Typedef

We can use typedef to define a reusable type:

1
2
3
    typedef int (^MyBlockType)(int, int);

    MyBlockType myBlock = ^(int a, int b){...};

Type cast

As other types, you can also type cast a block. The syntax is a little weired, though.

1
2
3
4
5
6
7
    void* someContext; // Probably comes from a function's argument;

    int (^myBlock)(int,int) = (int (^)(int,int))someContext;
    // block

    int (*myFnPt)(int,int) = (int (*)(int,int))someContext;
    // function pointer

Block in Objective-C class

property

Block in objective C is quite trivial:

1
2
3
@interface MyClass : NSObject
@property (nonatomic, copy) int(^myBlock)(int,int);
@end

Accessors and method arguments

However, it’s strange in method declaration and accessors.

1
2
-(int(^)(int,int)) getMyBlock;
-(void) setMyBlock: (int(^)(int a, int b)) inputBlock;

The syntax is weird because Apple uses type cast syntax as type declaration syntax. This is now the only way to use anonymous type in Objective-C method argument instead of using typedef. This syntax won’t work in other places, either.

Other syntaxs

Array

You can define a chunk of blocks like so:

1
2
3
4
    int(^myBlocks[5])(int,int);

    myBlocks[2]=^(int a, int b){...};
    myBlocks[2](3, 4); // excecute

Nested blocks

Nested block syntax is ugly:

1
2
3
4
    void(^(^myNestedBlock)())();

    typedef void(^VoidBlock)();
    VoidBlock(^myNestedBlock)(); // same, better

Readability of nested block without typedef is so horrible. typedef is strongly recommended.

That’s all for block syntax! There are still topics to discuss like memory management and grand central dispatch. I’ll discuss them in next few posts.

Comments