Ziel-C: Aufruf von Selektoren mit mehreren Argumenten

142

In MyClass.m habe ich definiert

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

und die entsprechende Erklärung in MyClass.h. Später möchte ich anrufen

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

in MyClass.m, aber ich erhalte eine ähnliche Fehlermeldung wie * Beenden der App aufgrund einer nicht erfassten Ausnahme 'NSInvalidArgumentException', Grund: '* - [MyClass myTest: withAtring:]: nicht erkannter Selektor an Instanz 0xe421f0 gesendet'

Ich habe einen einfacheren Fall mit einem Selektor versucht, der keine Argumente enthielt, die einen String auf die Konsole druckten, und der einwandfrei funktionierte. Was ist mit dem Code falsch und wie kann ich ihn beheben? Vielen Dank.

Stu
quelle
4
In Ihrem Beitrag werden Sie nach "mehreren Argumenten" gefragt, aber Sie verwenden nur eines. Jetzt bin ich gespannt, wie jemand es mit mehreren Argumenten machen würde, außer sie in ein Array / Diktat / was auch immer einzupacken.
RonLugge

Antworten:

137

Ihre Methodensignatur lautet:

- (void) myTest:(NSString *)

withAString ist zufällig der Parameter (der Name ist irreführend, es sieht so aus, als ob er Teil der Signatur des Selektors ist).

Wenn Sie die Funktion folgendermaßen aufrufen:

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

Es wird klappen.

Wie in den anderen Postern vorgeschlagen, möchten Sie die Methode möglicherweise umbenennen:

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

Und Ruf an:

[self performSelector:@selector(myTestWithAString:) withObject:myString];
Lyndsey Ferguson
quelle
2
Nachdem ich sehe, dass die Menschen von dieser Antwort profitiert haben, habe ich meine Antwort überprüft. Ich würde vorschlagen, dass der Aufruf einfach ist: - (void) testWithString: (NSString *) aString;
Lyndsey Ferguson
313

In Objective-C besteht die Signatur eines Selektors aus:

  1. Der Name der Methode (in diesem Fall wäre es 'myTest') (erforderlich)
  2. Ein ':' (Doppelpunkt) nach dem Methodennamen, wenn die Methode eine Eingabe hat.
  3. Ein Name und ':' für jede zusätzliche Eingabe.

Selektoren haben keine Kenntnis von:

  1. Die Eingabetypen
  2. Der Rückgabetyp der Methode.

Hier ist eine Klassenimplementierung, bei der die Methode performMethodsViaSelectors die anderen Klassenmethoden über Selektoren ausführt:

@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

Die Methode, für die Sie einen Selektor erstellen möchten, hat eine einzige Eingabe. Sie würden also einen Selektor dafür erstellen:

SEL myTestSelector = @selector(myTest:);
Shane Arney
quelle
3
Gute Antwort. Zur Verdeutlichung MUSS der Selektorname mindestens einen Teil haben, der einen Parameter annehmen kann oder nicht - wenn dies der Fall ist, muss er einen Doppelpunkt haben. Selektornamen mit zwei oder mehr Teilen MÜSSEN nach JEDEM Teil einen Doppelpunkt haben - es ist nicht zulässig, einen Selektor der Form "-useFoo: andBar: toDoSomething" zu haben.
Quinn Taylor
Danke dafür. Ich habe eine Weile damit zu kämpfen, freue mich über die Hilfe!
James Hall
Wie wäre es mit den Eingabeparametern, die Ganzzahlen sind? Was ist in diesem Fall zu tun?
Hoang Pham
1
Sie müssen die Ganzzahl in ein NSNumber-Objekt einschließen (siehe developer.apple.com/library/ios/#documentation/Cocoa/Reference/… ) und den Ganzzahlwert im Hauptteil der aufgerufenen Methode abrufen. Es kann ein bisschen ausführlich sein (und ich habe keinen besseren Weg gefunden, es zu umgehen), aber es funktioniert gut.
Shane Arney
30
+100: Das ist großartig! Ich wusste nicht, ob ich mehrere "withObject:" - Parameter verwenden kann. Ich würde dies hundertmal positiv bewerten, wenn ich könnte ...
FreeAsInBeer
13

@ Shane Arney

performSelector:withObject:withObject:

Möglicherweise möchten Sie auch erwähnen, dass diese Methode nur zum Übergeben von maximal 2 Argumenten dient und nicht verzögert werden kann. (wie performSelector:withObject:afterDelay:).

Ein bisschen komisch, dass Apple nur 2 zu sendende Objekte unterstützt und es nicht allgemeiner macht.

Lirik
quelle
2
Danke für die Information. Ich konnte die Verzögerung nicht zum Arbeiten bringen und jetzt weiß ich warum. Zu Ihrer Information, um die Grenze von zwei Objekten zu umgehen, habe ich ein Array übergeben und es dann in der Methode verwendet.
JScarry
7

Ihr Code hat zwei Probleme. Einer wurde identifiziert und beantwortet, der andere jedoch nicht. Das erste war, dass Ihrem Selektor der Name seines Parameters fehlte. Selbst wenn Sie dies beheben, wird in der Zeile dennoch eine Ausnahme ausgelöst, vorausgesetzt, Ihre überarbeitete Methodensignatur enthält immer noch mehr als ein Argument. Angenommen, Ihre überarbeitete Methode lautet wie folgt:

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

Das Erstellen von Selektoren für Methoden mit mehreren Argumenten ist vollkommen gültig (z. B. @selector (myTestWithString: compareTo :)). Mit der performSelector-Methode können Sie jedoch nur einen Wert an myTest übergeben, das leider mehr als einen Parameter enthält. Es wird ein Fehler ausgegeben und Sie werden darauf hingewiesen, dass Sie nicht genügend Werte angegeben haben.

Sie können Ihre Methode jederzeit neu definieren, um eine Sammlung als einzigen Parameter zu verwenden:

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

Es gibt jedoch eine elegantere Lösung (für die kein Refactoring erforderlich ist). Die Antwort ist, NSInvocation zusammen mit seinen setArgument:atIndex:und invokeMethoden zu verwenden.

Ich habe einen Artikel geschrieben, einschließlich eines Codebeispiels , wenn Sie weitere Details wünschen. Der Fokus liegt auf dem Einfädeln, aber die Grundlagen gelten weiterhin.

Viel Glück!

Zack
quelle
3

Ihre Methodensignatur macht keinen Sinn. Sind Sie sicher, dass es sich nicht um einen Tippfehler handelt? Ich bin mir nicht sicher, wie es überhaupt kompiliert wird, obwohl Sie vielleicht Warnungen erhalten, die Sie ignorieren?

Wie viele Parameter erwarten Sie für diese Methode?

Rob Napier
quelle
Entschuldigung, dass Sie schreiben. Ich habe es abgetippt und versucht, es einfacher zu machen, anstatt meinen Code zu kopieren und einzufügen, aber ich habe dabei einen Fehler gemacht. Ich erwarte, dass diese Methode einen Parameter annimmt. die Zeichenfolge, die ich drucken möchte.
Stu
2

Denken Sie, die Klasse sollte definiert werden als:

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

Sie haben nur einen einzigen Parameter, daher sollten Sie nur einen einzigen haben:

Vielleicht möchten Sie auch% @ in Ihrem NSLog verwenden - es ist nur eine gute Angewohnheit, sich darauf einzulassen - und dann jedes Objekt ausschreiben - nicht nur Zeichenfolgen.

Grouchal
quelle
-1

iOS-Benutzer erwarten auch Autokapitalisierung: In einem Standardtextfeld wird der erste Buchstabe eines Satzes in einer Sprache, bei der zwischen Groß- und Kleinschreibung unterschieden wird, automatisch großgeschrieben.

Sie können entscheiden, ob Sie solche Funktionen implementieren möchten oder nicht. Es gibt keine dedizierte API für eine der gerade aufgelisteten Funktionen, daher ist die Bereitstellung dieser Funktionen ein Wettbewerbsvorteil.

Laut Apple-Dokument ist für diese Funktion und eine andere erwartete Funktion in einem benutzerdefinierten Keyboard keine API verfügbar. Sie müssen also Ihre eigene Logik herausfinden, um dies zu implementieren.

Kannan Prasad
quelle