11

I'm a beginner to Objective C and iOS development, but a 13-year .NET veteran. I'm having a hard time mentally diagramming the following declaration, which came from the Programming with Objective C guide:

void (^(^a)(void (^) (void))) (void) = ...

It's used as an example of why one should use typedef to define blocks, but I want to understand what I'm looking at to better get a feel for block definition syntax in the first place.

Here's how I've diagrammed it so far:

enter image description here

Where I'm running into problems is that here is how I understand the basic syntax:

[return_val] (^[block_name]) ([block_args]) = ...

If that's the case, then what I have is a block that returns void and has no arguments, but is named (^a) (void (^) void). Meaning the name of my block, rather than being a straight string, is itself a block.

Clearly I'm missing something here. Can someone please shed some light on it? According to the site, it simplifies to this:

typedef void (^SimpleBlock) (void);
SimpleBlock (^complexBlock) (SimpleBlock) = ...

I'm just missing how.

Edit: The third void should have been in parentheses. I fixed that. It's wrong in the image, but I didn't feel like redoing the entire image just for that. :) If it turns out to be the source of my problem, I will fix it here.

Community
  • 1
  • 1
Ari Roth
  • 5,392
  • 2
  • 31
  • 46
  • Are you sure `void (^(^a)(void (^) void)) (void)` is correct? Shouldn't the 3rd `void` be in parentheses? Where in the link you referenced is this frmm? – rmaddy Feb 27 '14 at 01:49
  • You're right. I'll fix it in the message. That said, I don't think it would change anything in a material way here, would it? (Also, the link is in the post in the first paragraph.) – Ari Roth Feb 27 '14 at 01:57
  • There is reasonably good video tutorial on YouTube. I found it helpful. http://www.youtube.com/watch?v=9FWqh24b9oE – thatzprem Feb 27 '14 at 06:55
  • 1
    There is a really great blogpost by Nils Hayat that explains block syntax very clearly, building up understanding from the concept of C declarators. Link: [Block Syntax Explained](http://nilsou.com/blog/2013/08/21/objective-c-blocks-syntax/). *This may not be much of an answer, but this blogpost is so good I wanted to make it prominent.* – Nikita Kukushkin Feb 27 '14 at 07:00
  • A typedef or two works wonders to make the code more readable. – gnasher729 Jan 20 '15 at 08:48

2 Answers2

10

In your example you're missing some parentheses for the third void

void (^(^a)(void (^)(void)))(void)

Now let's break it down. The basic syntax to return a block from a function is:

void (^f())(void) { 
    return ^{}; 
}

In this example, the returned block takes no arguments and returns void.

Now let's build your example.

void     (^myBlock)(void);                       // Block returning void, taking no args
void     (^myBlock)(void (^)(void));             // Block returning void, taking block as arg
int      (^myBlock)(void (^)(void));             // Block returning int, taking block as arg
void (^  (^myBlock)(void (^)(void))  )(void);    // Block returning block, taking block as arg

I've aligned the central part in each line to make it easier to read. So the difficult part seems to be returning a block. In the last line we used the syntax I described earlier to return a block from a function.

Obviously the typedefs make it much easier to read.

EDIT:
Consider this example where, in the first line, i replace int for a block with the intuitive return syntax:

void (^ )(void) (^myBlock)(void (^)(void));          // Syntax we 'intuitively would use'
void (^         (^myBlock)(void (^)(void))  )(void); // Official syntax

I'm not 100% sure of what I'm about to say, but my suspicion is that the reason for this weird syntax is so that the parser in the compiler doesn't get confused. The first 'intuitive' syntax would make the compiler think that we have a block taking no arguments returning void, and the remaining characters would be considered syntax error.

In my opinion, syntax is something you don't question too much (you can criticize it, of course) because it's part of the design on a language, and we have to follow the rules (set by some hopefully smart engineers) for our code to compile.

Merlevede
  • 8,140
  • 1
  • 24
  • 39
  • 1
    While I understand all of this, the question that comes to mind is why isn't the final syntax something like `void (^)(void) (^myBlock)(void (^)(void));` where the first part `void (^)(void)` is the return value? I think the confusing part is why the syntax for returning an `int` is so different from returning a block. – rmaddy Feb 27 '14 at 02:07
  • Yeah, that's the problem I'm having. So as I read that, I see the second example as "a block named myBlock, that takes an unnamed argument which is a block that takes no arguments and returns a void". Therefore, reading the last line, I see "an unnamed block that takes an argument named myBlock that returns a void and takes an unnamed block with no arguments and returning void, which then returns void". This is a little wonky to me. Shouldn't it be something like `void (^finalBlock(^myBlock)(void (^) (void)))(void)`? Or is this just something I have to adjust to? – Ari Roth Feb 27 '14 at 02:17
  • Forgive me, but with all those parentheses I was immediately taken back several decades to LISP. :-) – mharper Feb 27 '14 at 02:35
0
void (^(^a)(void (^) (void))) (void)

Break these syntax in several pieces:

  1. a is a variable.
  2. it can be dereferenced like c pointer "*" by the syntax "^" : ^a.
  3. (^a)(void (^) (void) is a block named a and takes a block (void (^) (void) as parameter.
  4. it's return value can be dereferenced to yield a block information : ^(^a)(void (^) (void)). (and by implication the return value is therefore a block pointer)
  5. this returned block take no parameter : (^(^a)(void (^) (void))) (void).
  6. and this returned block doesn't need return value : void (^(^a)(void (^) (void))) (void)

So let's say (^a) (void (^) void) is not Meaning the name of my block, rather than being a straight string, is itself a block.. The block literal doesn't have to be

 [return_val] (^[block_name]) ([block_args])

the complier will take the code after a caret as a block.

johnMa
  • 3,291
  • 24
  • 37