web-dev-qa-db-fra.com

Objective-C: Appel de sélecteurs avec plusieurs arguments

Dans MyClass.m, j'ai défini

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

et la déclaration appropriée dans MyClass.h. Plus tard je veux appeler

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

dans MyClass.m mais j'obtiens une erreur semblable à * Arrêt de l'application en raison d'une exception non capturée 'NSInvalidArgumentException', raison: '* - [MyClass myTest: withAtring:]: sélecteur non reconnu envoyé à l'instance 0xe421f0'

J'ai essayé un cas plus simple avec un sélecteur qui ne prenait pas d'arguments qui imprimaient une chaîne à la console et qui fonctionnait parfaitement. Quel est le problème avec le code et comment puis-je résoudre ce problème? Merci.

139
Stu

Votre signature de méthode est:

- (void) myTest:(NSString *)

withAString se trouve être le paramètre (le nom est trompeur, on dirait qu'il fait partie de la signature du sélecteur).

Si vous appelez la fonction de cette manière:

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

Ça va marcher.

Mais, comme l'ont suggéré d'autres afficheurs, vous voudrez peut-être renommer la méthode:

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

Et appelez:

[self performSelector:@selector(myTestWithAString:) withObject:myString];
135
Lyndsey Ferguson

En Objective-C, la signature d'un sélecteur comprend:

  1. Le nom de la méthode (dans ce cas, il s'agirait de "myTest") (obligatoire)
  2. Un ':' (deux points) après le nom de la méthode si celle-ci a une entrée.
  3. Un nom et ':' pour chaque entrée supplémentaire.

Les sélecteurs n'ont aucune connaissance de:

  1. Les types d'entrée
  2. Le type de retour de la méthode.

Voici une implémentation de classe où la méthode performMethodsViaSelectors exécute les autres méthodes de classe à l'aide de sélecteurs:

@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

La méthode pour laquelle vous voulez créer un sélecteur a une seule entrée, vous devriez donc créer un sélecteur comme ceci:

SEL myTestSelector = @selector(myTest:);
308
Shane Arney

@Shane Arney

performSelector:withObject:withObject:

Vous voudrez peut-être aussi mentionner que cette méthode sert uniquement à transmettre 2 arguments au maximum et qu’elle ne peut pas être retardée. (comme performSelector:withObject:afterDelay:).

un peu bizarre que Apple ne supporte que 2 objets à envoyer et ne le rende pas plus générique.

13
Lirik

Votre code a deux problèmes. L'un a été identifié et a répondu, l'autre non. La première était qu'il manquait le nom de son paramètre à votre sélecteur. Cependant, même lorsque vous corrigez cela, la ligne lève toujours une exception, en supposant que votre signature de méthode révisée inclue toujours plus d'un argument. Disons que votre méthode révisée est déclarée comme:

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

La création de sélecteurs pour les méthodes prenant plusieurs arguments est parfaitement valide (par exemple, @selector (myTestWithString: comparativeTo :)). Cependant, la méthode performSelector vous permet uniquement de transmettre une valeur à myTest, qui comporte malheureusement plusieurs paramètres. Il y aura une erreur et vous dira que vous n'avez pas fourni suffisamment de valeurs.

Vous pouvez toujours redéfinir votre méthode pour prendre une collection en tant que paramètre:

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

Cependant, il existe une solution plus élégante (ne nécessitant pas de refactoring). La solution consiste à utiliser NSInvocation, avec ses méthodes setArgument:atIndex: et invoke.

J'ai écrit n article, y compris un exemple de code , si vous voulez plus de détails. L'accent est mis sur le filetage, mais les bases s'appliquent toujours.

Bonne chance!

7
Zack

Votre signature de méthode n'a aucun sens, êtes-vous sûr que ce n'est pas une faute de frappe? Je ne vois pas comment ça se fait, même si vous recevez peut-être des avertissements que vous ignorez?

Combien de paramètres attendez-vous de cette méthode?

3
Rob Napier

Pensez que la classe devrait être définie comme:

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

Vous n'avez qu'un seul paramètre, vous ne devriez donc en avoir qu'un seul:

Vous voudrez peut-être aussi envisager d'utiliser% @ dans votre NSLog - c'est simplement une bonne habitude à prendre - vous écrirez alors n'importe quel objet, pas seulement les chaînes.

2
Grouchal

les utilisateurs iOS attendent également une autocapitalisation: dans un champ de texte standard, la première lettre d'une phrase dans un langage sensible à la casse est automatiquement mise en majuscule.

Vous pouvez décider d'implémenter ou non de telles fonctionnalités. il n'y a pas d'API dédiée pour les fonctionnalités énumérées ci-dessus, donc leur fournir un avantage concurrentiel.

Un document Apple indique qu'aucune API n'est disponible pour cette fonctionnalité et d'autres fonctionnalités attendues dans un clavier personnalisé. vous devez donc connaître votre propre logique pour mettre cela en œuvre.

0
Kannan Prasad