Objective-C passing around … nil terminated argument lists

孤街浪徒 提交于 2019-11-26 10:56:08

问题


Having some issues with the ... in ObjectiveC.

I\'m basically wrapping a method and want to accept a nil terminated list and directly pass that same list to the method I am wrapping.

Here\'s what I have but it causes an EXC_BAD_ACCESS crash. Inspecting the local vars, it appears when otherButtonTitles is simply a NSString when it is passed in with 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];
}

How do I simply siphon from the argument incoming to the argument outgoing, preserving the exact same nil terminated list?


回答1:


You can't do this, at least not in the way you're wanting to do it. What you want to do (pass on the variable arguments) requires having an initializer on UIAlertView that accepts a va_list. There isn't one. However, you can use the addButtonWithTitle: method:

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

This is, of course, very problem-specific. The real answer is "you can't implicitly pass on a variable argument list to a method/function that does not have a va_list parameter". You must therefore find a way around the problem. In the example you gave, you wanted to make an alertView with the titles you passed in. Fortunately for you, the UIAlertView class has a method that you can iteratively call to add buttons, and thereby achieve the same overall effect. If it did not have this method, you'd be out of luck.

The other really messy option would be to make it a variadic macro. A variadic macro looks like this:

#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]; \
}

However, even with the variadic macro approach, you'd still need a custom macro for each time you wanted to do this. It's not a very solid alternative.




回答2:


How about constructing an NSInvocation object? Since arguments must be passed by pointer, you could pass the pointer to the nil-terminated list.

You could also iterate over the parameters using marg_list() and construct a nil-terminated list yourself.

These are just simple suggestions; I haven't tried them out.




回答3:


This is specific to the OP's UIAlertView-wrapping case, and tested only on iOS7: It appears that once a UIAlertView has been initialised with otherButtons:nil, and then has its style set to UIAlertViewStylePlainTextInput it doesn't call its delegate's alertViewShouldEnableFirstOtherButton: to validate input. I'm not sure if this is a bug or intended behaviour but it broke my principle of least astonishment. This is reproducible with the following (I'll assume the delegate's alertViewShouldEnableFirstOtherButton: is implemented):

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

The solution, since UIAlertView happily accepts otherButtons:nil, is to initialise UIAlertView with otherButtonTitles (which can be nil), and iterate over the variadic arguments, as above:

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


来源:https://stackoverflow.com/questions/2345196/objective-c-passing-around-nil-terminated-argument-lists

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!