Objective-C传递... nil终止参数列表

时间:2010-02-26 22:12:09

标签: objective-c variadic

在ObjectiveC中遇到...的一些问题。

我基本上包装了一个方法,并希望接受一个nil终止列表,并直接将相同的列表传递给我正在包装的方法。

这是我所拥有的,但它会导致EXC_BAD_ACCESS崩溃。检查本地变量时,当otherButtonTitles传递给NSString时,otherButtonTitles:@"Foo", nil]只是+ (void)showWithTitle:(NSString *)title message:(NSString *)message delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... { UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:title message:message delegate:delegate cancelButtonTitle:cancelButtonTitle otherButtonTitles:otherButtonTitles] autorelease]; [alert show]; } 时出现

nil

我如何简单地从传入参数传出到传出的参数,保留完全相同的{{1}}终止列表?

3 个答案:

答案 0 :(得分:40)

你不能这样做,至少不是你想要的方式。你想做什么(传递变量参数)需要在UIAlertView上有一个接受va_list的初始值设定项。没有一个。但是,您可以使用addButtonWithTitle:方法:

+ (void)showWithTitle:(NSString *)title
              message:(NSString *)message
             delegate:(id)delegate
    cancelButtonTitle:(NSString *)cancelButtonTitle
    otherButtonTitles:(NSString *)otherButtonTitles, ...
{
    UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:title
                                                     message:message
                                                    delegate:delegate
                                           cancelButtonTitle:cancelButtonTitle
                                           otherButtonTitles:nil] autorelease];
    if (otherButtonTitles != nil) {
      [alert addButtonWithTitle:otherButtonTitles];
      va_list args;
      va_start(args, otherButtonTitles);
      NSString * title = nil;
      while(title = va_arg(args,NSString*)) {
          [alert addButtonWithTitle:title];
      }
      va_end(args);
    }

    [alert show];
}

这当然是非常特定于问题的。真正的答案是“你不能隐式地将变量参数列表传递给没有va_list参数的方法/函数”。因此,您必须找到解决问题的方法。在您给出的示例中,您希望使用传入的标题创建一个alertView。幸运的是,UIAlertView类有一个方法可以迭代调用以添加按钮,从而实现相同的整体效果。如果它没有这种方法,那你就不走运了。

另一个非常混乱的选择是使它成为一个可变的宏。可变宏看起来像这样:

#define SHOW_ALERT(title,msg,del,cancel,other,...) { \
  UIAlertView *_alert = [[[UIAlertView alloc] initWithTitle:title message:msg delegate:del cancelButtonTitle:cancel otherButtonTitles:other, ##__VA_ARGS__] autorelease]; \
  [_alert show]; \
}

但是,即使使用可变参数宏方法,每次要执行此操作时仍需要自定义宏。这不是一个非常可靠的选择。

答案 1 :(得分:0)

如何构建NSInvocation对象?由于参数必须通过指针传递,因此您可以将指针传递给以nil结尾的列表。

您还可以使用marg_list()迭代参数并自行构建一个未终止的列表。

这些只是简单的建议;我还没试过。

答案 2 :(得分:0)

这是针对OP的UIAlertView - 包装案例,仅在iOS7上进行了测试:看起来UIAlertView初始化otherButtons:nil,然后设置其样式到UIAlertViewStylePlainTextInput它不会调用其委托的alertViewShouldEnableFirstOtherButton:来验证输入。我不确定这是一个错误或预期的行为,但它打破了我最不惊讶的原则。这可以通过以下方式重现(我假设委托的alertViewShouldEnableFirstOtherButton:已经实现):

UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Title" 
                                             message:@"message" 
                                            delegate:self         
                                   cancelButtonTitle:@"Cancel" 
                                   otherButtonTitles:nil];
[av setAlertViewStyle:UIAlertViewStylePlainTextInput];
[av addButtonWithTitle:@"OK"];
[av show];

由于UIAlertView乐意接受otherButtons:nil,因此解决方案是使用otherButtonTitles(可以是nil)初始化UIAlertView,并迭代可变参数,如上所述:

+ (void)showWithTitle:(NSString *)title
              message:(NSString *)message
             delegate:(id)delegate
    cancelButtonTitle:(NSString *)cancelButtonTitle
    otherButtonTitles:(NSString *)otherButtonTitles, ...
{
    UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:title
                                                     message:message
                                                    delegate:delegate
                                           cancelButtonTitle:cancelButtonTitle
                                           otherButtonTitles:otherButtonTitles] autorelease];

    // add your [alert setAlertViewStyle:UIAlertViewStylePlainTextInput] etc. as required here

    if (otherButtonTitles != nil) {
        va_list args;
        va_start(args, otherButtonTitles);
        NSString * title = nil;
        while(title = va_arg(args,NSString*)) {
            [alert addButtonWithTitle:title];
        }
        va_end(args);
    }

    [alert show];
}