2010-02-26 18 views
20

tener algunos problemas con el ... en ObjectiveC.ObjectiveC que pasa alrededor ... nula terminado listas de argumentos

Básicamente estoy envolviendo un método y quiero aceptar una lista terminada nil y pasar directamente esa misma lista al método que estoy envolviendo.

Aquí es lo que tengo pero causa un accidente EXC_BAD_ACCESS. La inspección de los VARs locales, que aparece cuando otherButtonTitles es simplemente una NSString cuando se pasa con 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]; 
} 

¿Cómo simplemente sifón del argumento de entrada al argumento saliente, preservando al mismo nil lista terminada exacta?

+1

el primer objeto en una lista de métodos variadic no es parte de la va_list en sí, por lo que se ve como un otherButtonTitles NSString. Es decir, la lista va_ solo comprende los objetos en la parte "...". – Don

+1

Dado que Objective-C es un superconjunto de C, cf. http://stackoverflow.com/questions/150543/forward-an-invocation-of-a-variadic-function-in-c. – Don

Respuesta

40

No se puede hacer esto, al menos no de la manera que está queriendo hacerlo. Lo que desea hacer (pasar los argumentos variables) requiere tener un inicializador en UIAlertView que acepte un va_list. No hay uno. Sin embargo, se puede utilizar el método de 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]; 
} 

Esto es, por supuesto, muy problema específico. La respuesta real es "no se puede pasar implícitamente una lista de argumentos variables a un método/función que no tenga un parámetro va_list". Por lo tanto, debe encontrar una manera de evitar el problema. En el ejemplo que proporcionó, quería hacer una vista de alerta con los títulos que pasó. Afortunadamente para usted, la clase UIAlertView tiene un método al que puede llamar iterativamente para agregar botones, y así lograr el mismo efecto general. Si no tuviera este método, no tendrías suerte.

La otra opción muy sucia sería hacer que una macro variadic. Una macro variadic se ve así:

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

Sin embargo, incluso con el enfoque macro variadic, todavía se necesitaría una macro personalizada para cada vez que quería hacer esto. No es una alternativa muy sólida.

+1

Un enfoque genial, pero esperaba utilizar esta técnica en otros lugares donde un método como este puede no existir para ayudar con la lista de argumentos. Realmente, ¿no hay forma de crear una lista terminada en cero de forma dinámica? –

+0

@Squeegy, puede falsificar una 'va_list' (ver el enlace cocoawithlove en el comentario de @ Don), pero a menos que el método que desea invocar tome' va_list' (en oposición a '...'), no puede usar eso. –

+0

Afortunadamente, la solución de Dave funciona para su situación específica. – Don

0

¿Qué hay de la construcción de un objeto NSInvocation? Como los argumentos deben pasar por un puntero, puede pasar el puntero a la lista terminada en cero.

También puede repetir los parámetros usando marg_list() y construir usted mismo una lista con terminación nula.

estas son sugerencias simples; No los probé.

+0

"' NSInvocation' no admite invocaciones de métodos con números variables de argumentos o argumentos 'union'." - http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSInvocation_Class/Reference/Reference.html#//apple_ref/doc/uid/20000212-1804 –

+0

Bueno, eso mata eso ¡sugerencia! – Don

+0

Creo que puedo hacer algo como esto para repetir la lista: http://en.wikipedia.org/wiki/Variadic_function#Variadic_functions_in_C.2C_Objective-C.2C_C.2B.2B.2C_and_D pero ¿cómo utilizo 'marg_list() 'para generar uno dinámicamente puedo conectarme de nuevo. –

0

Esto es específico para el caso -RECUBRIMIENTO del PO UIAlertView, y probado sólo en iOS7: Parece ser que una vez al UIAlertView se ha inicializado con otherButtons:nil, y después se ha fijado su estilo a UIAlertViewStylePlainTextInput no llama de alertViewShouldEnableFirstOtherButton: su delegado a validar la entrada. No estoy seguro de si esto es un error o un comportamiento intencionado, pero rompió mi principio de menor asombro.Esta es reproducible con la siguiente (voy a asumir es implementado de alertViewShouldEnableFirstOtherButton: el delegado):

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

La solución, ya que UIAlertView acepta felizmente otherButtons:nil, es para inicializar UIAlertView con otherButtonTitles (que puede ser nulo), y iterar sobre los argumentos variadic, como antes:

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

Si bien esta es información interesante y definitivamente debe ser reportada en el [reportero de errores] de Apple (http://bugreport.apple.com/) no es una respuesta a esta publicación. Según lo entiendo, no proporciona información adicional a la pregunta formulada. –

+0

Ya informado. Estoy de acuerdo, en parte; es una corrección de la respuesta aceptada en el lugar donde Google me trajo cuando estaba luchando con el envoltorio variada de UIAlertView. Quizás ayudará a alguien más ... –

Cuestiones relacionadas