3
votes

I've decided to try to use blocks for control flow in Objective-C and am running into some issues with calling multiple blocks inline.

I've got an OOBoolean which is a wrapper to a BOOL primitive, and provides these methods:

+ (id) booleanWithBool: (BOOL) boolPrimitive;

- (id) initWithBool: (BOOL) boolPrimitive;

- (void) ifTrueDo: (void (^) ()) trueBlock 
        ifFalseDo: (void (^) ()) falseBlock;

- (void) ifTrueDo: (void (^) ()) trueBlock;

- (void) ifFalseDo: (void (^) ()) falseBlock;

I have no problem using this class like so:

OOBoolean* condition = [OOBoolean booleanWithBool: (1 + 1 == 2)];

id trueBlock = ^(){
    NSLog(@"True.");
};

id falseBlock = ^(){
    NSLog(@"False.");
};

[condition ifTrueDo: trueBlock ifFalseDo: falseBlock];

And I get a result of "True.". But I keep getting syntax errors when trying this instead:

OOBoolean* condition = [OOBoolean booleanWithBool: (1 + 1 == 2)];

[condition ifTrueDo:(void (^)()) {
    NSLog(@"True");
} ifFalseDo:(void (^)()) {
    NSLog(@"False");
}];

Is it not possible to define multiple blocks anonymously and pass them to a method that takes multiple block arguments? If so, that's kind of a let down.

2
Interesting idea, though I'm thinking this is much less efficient at runtime than having the compiler optimize conditionals for you.Hyperbole
I'm actually interested in understanding the efficiency differences here. Clearly this is less efficient due to the additional message send, and I understand there's some performance considerations with blocks and enclosing variables outside ifTrue ifFalse message call, but I'm curious how much of an impact this would really have.donalbain
The easiest way to think about this is to consider how many stack frames you're pushing. The more stack frame you have the less efficient your code is (probably). I don't know a lot about compiler theory but I can guess that using the standard if() else construct results in optimized program flow using certain options at compile time.Hyperbole
Are you familiar with other languages that do this? Do you know if there are similar performance penalties in those? I'm thinking Smalltalk specifically, but I wonder if compiler optimizations could be done for this format as well (which I imagine they must be in something like Smalltalk since it's the only way of doing control flow). I have to admit I'm very ignorant on the implementation issues as well, I just like the style.donalbain
I can't comment on Smalltalk (I knew you were going to mention Smalltalk) but any compiler worth its salt knows how to identify hotspots in code and common decision structure optimizations, like unrolling loops. IMHO, I think the Objective C compiler won't be able to optimize your OOBoolean class behavior.Hyperbole

2 Answers

7
votes

It's possible.

You simply had way too many parentheses in there. Try this:

[condition ifTrueDo:^() { NSLog(@"True"); } ifFalseDo:^() { NSLog(@"False"); } ];

EDIT: Your block syntax is slightly incorrect.

If you want to include return type and parameters, you should use something closer to this:

[self ifTrueDo:^ void (void) { NSLog(@"True"); } ifFalseDo:^ void (void) { NSLog(@"False"); } ];

In english:

^ [return type] ([parameter list]) {[block content]}

1
votes

The problem is that your method declaration expects a block returning void (nothing):

- (void) ifTrueDo: (void (^) ()) trueBlock 
        ifFalseDo: (void (^) ()) falseBlock;

However, you later call this passing in blocks with the signature of (id^()()):

[condition ifTrueDo:(id (^)()) {
    NSLog(@"True");
}         ifFalseDo:(id (^)()) {
    NSLog(@"False");
}];

Just get rid of the "id" part like the following - note: and I tried this and it compiles without warnings:

[condition ifTrueDo:^{
          NSLog(@"True");
     }
     ifFalseDo:^{
          NSLog(@"False");
     }
 ];