首页 > 解决方案 > 带有 actionBlock 的宏会导致编译器错误

问题描述

我已经定义了一组宏来在创建UIActionControllerUIAlertAction语句时直观地简化我的代码。宏工作正常,但有一种情况会导致编译器错误。尽管有一个简单的解决方法,但我试图了解为什么会发生这些错误以及如何重写宏以消除对解决方法的需要。

下面定义了两个宏。包含第一个宏仅是为了编译示例代码。

我的问题涉及第二个宏。它有两个论点。一个是字符串,另一个是actionBlock(对于 a 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
                     );

标签: iosobjective-cxcodemacroscompletionhandler

解决方案


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

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

如果该链接成为孤立链接,那么重点是:

预处理器支持可变参数宏;可以接受无限量参数的宏。它们的实现方式是通过VA_ARGS标识符提供任何额外的参数(在宏定义中由 ... 表示) ;VA_ARGS被替换为用逗号分隔的所有额外参数。[这将] 一段带有无保护逗号的代码块传递给宏。

那篇文章归功于 2012 年的这个早期解决方案: https ://stackoverflow.com/a/13842612/1578823所以,+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;
                     );

推荐阅读