12

I find in Apple's document Working with Blocks that the syntax to define a block that returns the result of multiplying two values:

double (^multiplyTwoValues)(double, double);

is different than defining a block that takes another block as an argument and returns yet another block:

void (^(^complexBlock)(void (^)(void)))(void);

Why is the second syntax not void (^)(void)(^complexBlock)(void (^)(void))?

jscs
  • 63,694
  • 13
  • 151
  • 195
lu yuan
  • 7,207
  • 9
  • 44
  • 78
  • "Why the second syntax is not..." because the block syntax needs to fit with the rest of the C language which is 45 years old and everybody expects the syntax to be the way it is. Swift has much nicer block syntax, use that if you don't like the syntax of Objective-C. – Abhi Beckert Nov 27 '14 at 05:48

2 Answers2

21

This is just how C syntax works. The Block syntax is based on that of function pointers, which boils down to Dennis Ritchie's idea that "the declaration of a thing should look like the use of that thing".

If you were to use the "complex Block" you defined, and then to also call the returned Block in the same line, it would look like this:

complexBlock(void (^argBlock)(void){ /*...*/ })();
//           ^ Argument (a literal Block) to the main Block
//                                              ^ Invocation of the returned Block

Further, the parsing of C declarations follows a so-called "right-left rule". The first step is "find the identifier". For your declaration, that's complexBlock.

void (^(^complexBlock)(void (^)(void)))(void);
//       |     !    | Found identifier: "complexBlock is..."

Then, look to the right. We hit a closing parenthesis, so this is the end of a declaration "unit".

void (^(^            )(void (^)(void)))(void);
//       |           ! Right parenthesis closes a part of the declaration

Go back to the beginning of the current part, and read leftwards until an opening parenthesis. We find the caret indicating a Block type. Keep reading left, and find an opening parenthesis, closing off this part of the declaration.

void (^(^             (void (^)(void)))(void);
//     |!            | "...a Block..."

Next, go right again. Here we find an opening parenthesis, indicating the start of a parameter list. Skip the parameter list since you're concerned with the return type, but it's parsed as a standalone declaration would be.

void (^              (void (^)(void)))(void);
//     |             !              | "...taking something or other and returning..."

Now that we've consumed the parameter list:

void (^                              )(void);
//     |                            |

continue moving right, and we hit a closing parenthesis:

void (^                              )(void);
//     |                             !

So, again, back up to the beginning of the current part and move left where we find the Block caret.

void (^                               (void);
//    !                              | "...a Block.."

Here's the key part for your question about this declaration:

Moving left, again we find an opening parenthesis, so we return to moving right. That's why the return Block's parameter list goes at the end of the declaration.

void (                                (void);
//   !                               | Left parenthesis closes part of declaration,
//                                     **now move rightwards again**

Having gone through all that, the rest should be self-evident.

Incidentally, the page I linked to about the right-left rule has a few demonstrations like mine, one of which involves function pointers. You may also be amused by http://cdecl.org, which is an online implementation of a program that parses C declarations and can help you understand the woolier varieties.

Community
  • 1
  • 1
jscs
  • 63,694
  • 13
  • 151
  • 195
2

Obj-C block syntax is pretty hard to read, this can be simplified a bit with the use of typedefs.

//setup
typedef void (^ReturnedBlock)(void);
ReturnedBlock retBlock = ^void(void){};

typedef void (^ParamBlock)(void);
ParamBlock paramBlock = ^void(void){};

//the thing you want to do
ReturnedBlock (^someBlock)(ParamBlock) = ^ReturnedBlock(ParamBlock param){

    return retBlock;
};

//perform the block
ReturnedBlock r = someBlock(paramBlock);
Nick
  • 2,361
  • 16
  • 27
  • Your ReturnedBlock has void return, and ParamBlock has void parameter. – lu yuan Nov 27 '14 at 05:12
  • i realize that, you can customize the typedefs however you want. The main idea is to use typedefs to make blocks more readable and understandable, then the syntax matters less. – Nick Nov 27 '14 at 05:32