I have defined a set of macros to visually simplify my code when creating UIActionController
and UIAlertAction
statements. The macros work fine, but there is one situation that causes compiler errors. Although there is a simple workaround, I'm trying to understand why these errors occur and how to rewrite the macro to eliminate the need for the workaround.
There are two macros defined, below. The first macro is included only to allow the example code to compile.
My issue concerns the second macro. It has two arguments. One is a string and the other is an actionBlock
(for a completionHandler
). If the actionBlock
contains a statement with an array or a method call with multiple arguments (that resemble an array), the compiler complains. The errors can be eliminated by adding a pair of parentheses, as shown.
- (void)macroTest
{
#define ALERT_CREATE( title, msg ) \
UIAlertController *alertController = \
[UIAlertController alertControllerWithTitle:title \
message:msg preferredStyle:UIAlertControllerStyleAlert]
#define ALERT_DEFAULT_ACTION( title, actionBlock ) \
[alertController addAction:[UIAlertAction actionWithTitle:title \
style:UIAlertActionStyleDefault \
handler:^(UIAlertAction *action) { actionBlock } ]]
// Example code
ALERT_CREATE( @"Alert Title", @"Alert Message" );
// This example is fine (no errors):
ALERT_DEFAULT_ACTION( @"Action 1 Title" , /* actionBlock */
int i = 1;
i++;
);
// This results in error: Use of undeclared identifier 'ALERT_DEFAULT_ACTION'
ALERT_DEFAULT_ACTION( @"Action 2a Title" ,
// Another error: Too many arguments provided to function-like macro invocation
NSArray *test = @[ @"1", @"2" ]; // not okay
);
ALERT_DEFAULT_ACTION( @"Action 2b Title" ,
// The same code as 2a, but with parens, is okay
NSArray *test = (@[ @"1", @"2" ]); // this is okay (with parens)
);
ALERT_DEFAULT_ACTION( @"Action 3 Title" ,
// Parentheses are needed here, too, to avoid error
NSString *str1 = [NSString stringWithFormat:(@"format %@", @"arg")]; NSString *str2 = ([NSString stringWithFormat:@"format %@", @"arg"]);
);
`enter code here`// The next line displays the alert. It's commented out to avoid errors in this example.
// [self presentViewController:alertController animated:YES completion:nil];
}
Why does the compiler complain and how can I rewrite the second macro so I don't have to add the parentheses to some of the statements in its actionBlock? I'm using Xcode 11.3.
UPDATE: It also confuses Xcode's indentation logic if there's a conditional in the actionBlock. To wit:
ALERT_DEFAULT_ACTION( @"Action 4a Title" ,
// code here, correct indentation
);
ALERT_DEFAULT_ACTION( @"Action 4b Title" ,
if ( true ) {
// code here, incorrect indentation
} // incorrect indenation
);