带有 actionBlock 的宏会导致编译器错误

Macro with an actionBlock causes compiler errors

我定义了一组宏,以便在创建 UIActionControllerUIAlertAction 语句时从视觉上简化我的代码。宏工作正常,但有一种情况会导致编译器错误。虽然有一个简单的解决方法,但我试图了解为什么会出现这些错误以及如何重写宏以消除对解决方法的需求。

下面定义了两个宏。包含 first 宏只是为了允许示例代码编译。

我的问题涉及 second 宏。它有两个参数。一个是字符串,另一个是 actionBlock(对于 completionHandler)。如果 actionBlock 包含带有数组的语句或带有多个参数(类似于数组)的方法调用,编译器会报错。可以通过添加一对括号来消除错误,如图所示。

- (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];
}

为什么编译器会报错,我如何重写第二个宏,这样我就不必在它的 actionBlock 中的某些语句中添加括号?我正在使用 Xcode 11.3.

更新: 如果 actionBlock 中有条件,它还会混淆 Xcode 的缩进逻辑。即:

ALERT_DEFAULT_ACTION( @"Action 4a Title" ,
                     // code here, correct indentation
                     );
ALERT_DEFAULT_ACTION( @"Action 4b Title" ,
                     if ( true ) {
    // code here, incorrect indentation
} // incorrect indenation
                     );

我的问题应该被标记为重复,因为有几个类似的问题和答案。诀窍是使用好的搜索词(提示,搜索:在宏中传递块)。

解决方案是使用 variadic 函数(“...”和 VA_ARGS)。宏的最后一个参数吞噬了块中的所有内容,包括逗号,如这篇优秀文章中所述:将块作为参数发送给宏 https://mort.coffee/home/obscure-c-features/

如果 link 成为孤儿,请注意以下重点:

The pre-processor supports variadic macros; macros which can take an unlimited amount of arguments. The way they are implemented is that any extra arguments (indicated by ... in the macro definition) are made available through the VA_ARGS identifier; VA_ARGS is replaced with all the extra arguments separated by commas. [This passes] a block of code, with unguarded commas, to a macro.

post 归功于 2012 年的这个早期解决方案: 所以,在那里 +1。

这是我的固定宏:

#define ALERT_DEFAULT_ACTION( title, ... /* actionBlock */ ) \
    [alertController addAction:[UIAlertAction actionWithTitle:title \
        style:UIAlertActionStyleDefault \
        handler:^(UIAlertAction *action) { {__VA_ARGS__} /* actionBlock */ }]]

这不能解决错误缩进括号的问题(例如,在 if 语句中)。这对 Xcode.

来说太混乱了
ALERT_DEFAULT_ACTION( @"title",
                     NSArray *test = @[ @"1", @"2" ]; // commas okay now
                     if ( true ) {
    test = test; // incorrect indentation
} // incorrect indentation
                     test = test;
                     );