Objective-C передает списки аргументов с нулевым завершением

Возникли некоторые проблемы с ... в 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 завершенный список?


person Alex Wayne    schedule 26.02.2010    source источник
comment
Первый объект в вариативном списке методов не является частью самого va_list, поэтому вы видите otherButtonTitles как NSString. То есть va_list содержит только объекты в части ....   -  person Don    schedule 27.02.2010
comment
Поскольку Objective-C является надмножеством C, ср. stackoverflow .com/questions/150543/.   -  person Don    schedule 27.02.2010


Ответы (3)


Вы не можете этого сделать, по крайней мере, не так, как вы хотите это сделать. То, что вы хотите сделать (передать переменные аргументы), требует наличия инициализатора 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]; \
}

Однако даже при использовании подхода с вариативным макросом вам все равно потребуется собственный макрос каждый раз, когда вы захотите это сделать. Это не очень надежная альтернатива.

person Dave DeLong    schedule 26.02.2010
comment
Классный подход, но я надеялся использовать эту технику в других местах, где такого метода может не быть, чтобы помочь со списком аргументов. Неужели нет способа динамически создать список с нулевым завершением? - person Alex Wayne; 27.02.2010
comment
@Squeegy, возможно, вы можете подделать va_list (см. ссылку Cocowithlove в комментарии @Don), но если метод, который вы хотите вызвать, не принимает va_list (в отличие от ...), вы не можете его использовать. - person Dave DeLong; 27.02.2010
comment
К счастью, решение Дейва подходит для вашей конкретной ситуации. - person Don; 27.02.2010
comment
Если вам нужно принять список переменных аргументов (как довольно хорошо ответил Дэйв выше), а затем создать вызов невариативного API, используйте NSInvocation. - person bbum; 27.02.2010
comment
Если кто-то хочет увидеть это решение в рабочем коде, я использовал его для создания класса, через который я теперь буду направлять все свои потребности в отображении предупреждений. Помогите себе и/или добавьте улучшения на github.com/clozach/CLAlertViewManager. - person clozach; 18.09.2011

Как насчет создания объекта NSInvocation? Поскольку аргументы должны передаваться указателем, вы можете передать указатель на список с нулевым завершением.

Вы также можете перебирать параметры, используя marg_list(), и самостоятельно создавать список с нулевым завершением.

Это всего лишь простые предложения; Я не пробовал их.

person Don    schedule 26.02.2010
comment
NSInvocation не поддерживает вызовы методов с переменным числом аргументов или с union аргументами. - developer.apple.com/mac/library/documentation/Cocoa/Reference/ - person Dave DeLong; 27.02.2010
comment
Ну, это убивает это предложение! - person Don; 27.02.2010
comment
Я думаю, что могу выполнить итерацию по списку примерно так: noreferrer">en.wikipedia.org/wiki/, но как мне использовать marg_list() для динамического создания одного, который я могу передать обратно. - person Alex Wayne; 27.02.2010
comment
У Мэтта Галлахера есть способ создать поддельный вариативный список: cocoawithlove. com/2009/05/variable-argument-lists-in-cocoa.html Опять же, я не пробовал. - person Don; 27.02.2010

Это характерно для случая UIAlertView-обертки OP и проверено только на 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, состоит в том, чтобы инициализировать UIAlertView с otherButtonTitles (который может быть равен нулю) и перебирать аргументы с переменным числом аргументов, как указано выше:

+ (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];
}
person Robin Macharg    schedule 29.10.2013
comment
Хотя это интересная информация, и о ней обязательно следует сообщить в репортер Apple об ошибках, это не ответ на этот пост. Насколько я понимаю, он не предоставляет дополнительной информации к заданному вопросу. - person Nikolai Ruhe; 29.10.2013
comment
Уже сообщили. Я согласен, отчасти; это исправление принятого ответа в том месте, куда меня привел Google, когда я боролся с собственной вариативной оболочкой UIAlertView. Может еще кому поможет... - person Robin Macharg; 29.10.2013