I have no clue if what I am trying to do is possible or not. I am not experienced with blocks, just read a few tutorials, and really like the idea. My class AppointmentView.h (and m) is extending a ViewController. It contains a few properties which are members of another Class Appointment, which in turn has a few NSArrays. For the purpose of creating a grouped tableview, I check the values of the arrays, and then copy appropriately values that will be displayed (nothing too fancy or complicated). Normally I would go with if-else statements, and control appropriately, however after looking at blocks, it got me thinking if the following is doable:
[self setOutcomes: [[NSArray alloc] initWithObjects:
^{ return [myAppointment.CANCELED isEqualToString:@"NO"] == YES ?
[[NSString alloc] initWithString:@"Not Cancelled"] :
[[NSString alloc] initWithString:@"Cancelled"]; },
^{ return [myAppointment.CANCELED isEqualToString:@"NO"] == YES ?
[[NSString alloc] initWithString:@"No Cancellation reason"] :
[[NSString alloc] initWithString:myAppointment.CANCREASON]; },
^{ return [myAppointment EVENTS].length > 0 ?
[[NSString alloc] initWithString:myAppointment.EVENTS] :
[[NSString alloc] initWithString:@"No Events"]; },
^{ return [myAppointment SUMMARY].length > 0 ?
[[NSString alloc] initWithString:myAppointment.SUMMARY] :
[[NSString alloc] initWithString:@"No Summary"]; },
nil]];
This code compiles fine, but it crashes when run. My understanding is that this is wrong, because I am telling the enumeration to execute a piece of code every time. So this is not what I want. Then I tried the following:
[self setOutcomes: [[NSArray alloc] initWithObjects:
[[NSString alloc] initWithString: (NSString *) ^(void){
return [myAppointment.CANCELED isEqualToString:@"NO"] == YES ?
@"Not Cancelled" : @"Cancelled"; }],
[[NSString alloc] initWithString: (NSString *) ^(void){
return [myAppointment.CANCELED isEqualToString:@"NO"] == YES ?
@"No Cancellation reason" : myAppointment.CANCREASON; }],
[[NSString alloc] initWithString: (NSString *) ^(void){
return [myAppointment.EVENTS isEqualToString:@""] == YES ?
myAppointment.EVENTS : @"No Events"; }],
[[NSString alloc] initWithString: (NSString *) ^(void){
return [myAppointment.SUMMARY isEqualToString:@""] == YES ?
myAppointment.SUMMARY : @"No Summary"; }],
nil]];
Which although it compiles fine, crashes with the following error:
-[NSMallocBlock length]: unrecognized selector sent to instance 0x6bd68f0
I also tried typedefining a generic method which I could use, but didnt help either. So, I was wondering, is this possible ? I know that for this piece of code, I could simply use an if-else (like I normally would) and be done with it, but other circumstances, such a dynamic code generation option would be extremely useful.
I got the feeling that either NSString does not know how to handle this, and I should therefore extend it with an options lets say
initWithBlock:
Or I am missing something else here. Or it may just be not possible.... (?)
PS: I have just seen NSArray's enumerateUsingBlock: method, but I am not certain if this would serve my purpose in this case, as each entry in the array is completely different from the other, and a specific block would not fit the criteria.
If you need to make simple choices in the initializer you don't need blocks.
Use parens to enclose the ?: operator.
Updated to answer question of how to define and invoke a block at same spot
This unit test worked for me:
That's simple:
^{ … }
to define the block, then()
to invoke the block.