Objetivo-C: llamar a selectores con múltiples argumentos

142

En MyClass.m, he definido

- (void) myTest: (NSString *) withAString{
    NSLog(@"hi, %@", withAString);
}

y la declaración apropiada en MyClass.h. Luego quiero llamar

[self performSelector:@selector(mytest:withAString:) withObject: mystring];

en MyClass.m pero obtengo un error similar a * Finalización de la aplicación debido a la excepción no detectada 'NSInvalidArgumentException', razón: '* - [MyClass myTest: withAtring:]: selector no reconocido enviado a la instancia 0xe421f0'

Intenté un caso más simple con un selector que no tomó argumentos que imprimieron una cadena en la consola y que funcionó bien. ¿Qué tiene de malo el código y cómo puedo solucionarlo? Gracias.

Stu
fuente
44
Su publicación está preguntando sobre 'argumentos múltiples', pero solo usa uno. Ahora tengo curiosidad acerca de cómo alguien lo haría con múltiples argumentos, aparte de envolverlos en una matriz / dict / lo que sea.
RonLugge

Respuestas:

137

La firma de su método es:

- (void) myTest:(NSString *)

withAString resulta ser el parámetro (el nombre es engañoso, parece que es parte de la firma del selector).

Si llama a la función de esta manera:

[self performSelector:@selector(myTest:) withObject:myString];

Funcionará.

Pero, como han sugerido los otros carteles, es posible que desee cambiar el nombre del método:

- (void)myTestWithAString:(NSString*)aString;

Y llama:

[self performSelector:@selector(myTestWithAString:) withObject:myString];
Lyndsey Ferguson
fuente
2
Ahora que veo que las personas se han beneficiado de esta respuesta, he revisado mi respuesta; Sugeriría que la llamada sea simplemente: - (nulo) testWithString: (NSString *) aString;
Lyndsey Ferguson el
313

En Objective-C, la firma de un selector consiste en:

  1. El nombre del método (en este caso sería 'myTest') (obligatorio)
  2. A ':' (dos puntos) después del nombre del método si el método tiene una entrada.
  3. Un nombre y ':' para cada entrada adicional.

Los selectores no tienen conocimiento de:

  1. Los tipos de entrada
  2. El tipo de retorno del método.

Aquí hay una implementación de clase donde el método performMethodsViaSelectors realiza los otros métodos de clase por medio de selectores:

@implementation ClassForSelectors
- (void) fooNoInputs {
    NSLog(@"Does nothing");
}
- (void) fooOneIput:(NSString*) first {
    NSLog(@"Logs %@", first);
}
- (void) fooFirstInput:(NSString*) first secondInput:(NSString*) second {
    NSLog(@"Logs %@ then %@", first, second);
}
- (void) performMethodsViaSelectors {
    [self performSelector:@selector(fooNoInputs)];
    [self performSelector:@selector(fooOneInput:) withObject:@"first"];
    [self performSelector:@selector(fooFirstInput:secondInput:) withObject:@"first" withObject:@"second"];
}
@end

El método para el que desea crear un selector tiene una sola entrada, por lo que crearía un selector de esta manera:

SEL myTestSelector = @selector(myTest:);
Shane Arney
fuente
3
Buena respuesta. Para aclarar un poco, el nombre del selector DEBE tener al menos una parte, que puede o no tomar un parámetro; si lo hace, debe tener dos puntos. Los nombres de los selectores con dos o más partes DEBEN tener dos puntos después de CADA parte; no es legal tener un selector con la forma "-useFoo: andBar: toDoSomething".
Quinn Taylor
gracias por esto. He estado luchando con esto por un tiempo, ¡me alegro por la ayuda!
James Hall
¿Qué tal los parámetros de entrada son números enteros? ¿Qué hacer en este caso?
Hoang Pham
1
Deberá ajustar el número entero en un objeto NSNumber (consulte developer.apple.com/library/ios/#documentation/Cocoa/Reference/… ) y recuperar el valor entero en el cuerpo del método llamado. Puede ser un poco detallado (y no he encontrado una mejor manera de evitarlo) pero funciona bien.
Shane Arney
30
+100: ¡Esto es asombroso! No sabía si podía usar múltiples parámetros "withObject:". Votaría esto cien veces si pudiera ...
FreeAsInBeer
13

@Shane Arney

performSelector:withObject:withObject:

Es posible que también desee mencionar que este método es solo para pasar un máximo de 2 argumentos, y no se puede retrasar. (tal como performSelector:withObject:afterDelay:).

un poco extraño que apple solo admita 2 objetos para enviar y no lo hizo más genérico.

Lirik
fuente
2
Gracias por la info. No pude demorar el trabajo y ahora sé por qué. Para su información, para superar el límite de dos objetos, pasé una matriz y luego la usé en el método.
JScarry
7

Tu código tiene dos problemas. Uno fue identificado y respondido, pero el otro no. La primera fue que a su selector le faltaba el nombre de su parámetro. Sin embargo, incluso cuando arregla eso, la línea aún generará una excepción, suponiendo que la firma de su método revisado aún incluya más de un argumento. Digamos que su método revisado se declara como:

-(void)myTestWithString:(NSString *)sourceString comparedTo:(NSString *)testString ;

La creación de selectores para métodos que toman múltiples argumentos es perfectamente válida (por ejemplo, @selector (myTestWithString: compareTo :)). Sin embargo, el método performSelector solo le permite pasar un valor a myTest, que desafortunadamente tiene más de un parámetro. Se producirá un error y le dirá que no proporcionó suficientes valores.

Siempre puede redefinir su método para tomar una colección, ya que es el único parámetro:

-(void)myTestWithObjects:(NSDictionary *)testObjects ;

Sin embargo, hay una solución más elegante (que no requiere refactorización). La respuesta es utilizar NSInvocation, junto con sus setArgument:atIndex:y invokemétodos.

He escrito un artículo, incluido un ejemplo de código , si quieres más detalles. El enfoque está en el enhebrado, pero los conceptos básicos aún se aplican.

¡Buena suerte!

Zack
fuente
3

La firma de tu método no tiene sentido, ¿estás seguro de que no es un error tipográfico? No tengo claro cómo se está compilando, ¿aunque quizás recibas advertencias que ignoras?

¿Cuántos parámetros espera que tome este método?

Rob Napier
fuente
Lo siento, estás escribiendo. Lo escribí e intenté simplificarlo en lugar de copiar y pegar mi código, pero cometí un error en el proceso. Espero que este método tome un parámetro; la cadena que me gustaría imprimir.
Stu
2

Piensa que la clase debería definirse como:

- (void) myTestWithSomeString:(NSString *) astring{
    NSLog(@"hi, %s", astring);
}

Solo tiene un único parámetro, por lo que solo debe tener uno:

Es posible que desee considerar el uso de% @ en su NSLog también, es solo un buen hábito para entrar, luego escribirá cualquier objeto, no solo cadenas.

Grouchal
fuente
-1

Los usuarios de iOS también esperan autocapitalización: en un campo de texto estándar, la primera letra de una oración en un idioma que distingue entre mayúsculas y minúsculas se capitaliza automáticamente.

Puede decidir si implementa o no tales características; no hay una API dedicada para ninguna de las funciones que se acaban de enumerar, por lo que proporcionarlas es una ventaja competitiva.

El documento de Apple dice que no hay una API disponible para esta característica y alguna otra característica esperada en un teclado personalizado. Por lo tanto, debe encontrar su propia lógica para implementar esto.

Kannan Prasad
fuente