Wie kann ich mit Objective-C zur Laufzeit dynamisch einen Selektor erstellen?

93

Ich weiß, wie man einen SELzur Kompilierungszeit mit erstellt, @selector(MyMethodName:)aber ich möchte einen Selektor dynamisch aus einem erstellen NSString. Ist das überhaupt möglich?

Was ich tun kann:

SEL selector = @selector(doWork:);
[myobj respondsToSelector:selector];

Was ich tun möchte: (Pseudocode, das funktioniert offensichtlich nicht)

SEL selector = selectorFromString(@"doWork");
[myobj respondsToSelector:selector];

Ich habe die Apple API-Dokumente durchsucht, aber keinen Weg gefunden, der nicht auf der @selector(myTarget:)Syntax zur Kompilierungszeit beruht .

craigb
quelle

Antworten:

180

Ich bin kein Objective-C-Programmierer, nur ein Sympathisant, aber vielleicht brauchen Sie NSSelectorFromString . In der Laufzeitreferenz wird explizit erwähnt, dass Sie damit eine Zeichenfolge in einen Selektor konvertieren können.

Torsten Marek
quelle
5
Ich muss mein Google-Fu auffrischen. Genau das habe ich gesucht (oder auch nicht).
Craigb
Nun, ich hatte immer noch die Links in meinen Lesezeichen, seit ich vor ein paar Tagen die Objective-C 2.0-Dokumente gelesen habe.
Torsten Marek
40

Laut der XCode-Dokumentation macht Ihr Pseudocode es im Grunde richtig.

Am effizientesten ist es, SEL-Variablen zur Kompilierungszeit mit der Direktive @selector () Werte zuzuweisen. In einigen Fällen muss ein Programm jedoch zur Laufzeit möglicherweise eine Zeichenfolge in einen Selektor konvertieren. Dies kann mit der Funktion NSSelectorFromString erfolgen:

setWidthHeight = NSSelectorFromString(aBuffer);

Edit: Schade, zu langsam. : P.

Josh Gagnon
quelle
2
NSStringFromSelector(@"doWork")konvertiert es in die andere Richtung (nur zu
Ihrer Information
8
Ich denke du meinst, NSStringFromSelector (@selector (doWork))
jpswain
Und was macht dieser Selektor angeblich? Sollten wir nicht einen Block oder etwas angeben?
user4951
12

Ich muss sagen, dass es etwas komplizierter ist, als die Antworten der vorherigen Befragten vermuten lassen ... wenn Sie wirklich einen Selektor erstellen möchten ... nicht nur "einen anrufen", den Sie "herumliegen". .

Sie müssen einen Funktionszeiger erstellen, der von Ihrer "neuen" Methode aufgerufen wird. Für eine Methode wie würden [self theMethod:(id)methodArg];Sie also schreiben ...

void (^impBlock)(id,id) = ^(id _self, id methodArg) { 
     [_self doSomethingWith:methodArg]; 
};

und dann müssen Sie den IMPBlock dynamisch generieren , diesmal unter Übergabe von "self", the SELund allen Argumenten ...

void(*impFunct)(id, SEL, id) = (void*) imp_implementationWithBlock(impBlock);

und fügen Sie es Ihrer Klasse hinzu, zusammen mit einer genauen Methodensignatur für den gesamten Trottel (in diesem Fall "v@:@"void return, Objektaufrufer, Objektargument)

 class_addMethod(self.class, @selector(theMethod:), (IMP)impFunct, "v@:@");

Sie können einige gute Beispiele für diese Art von Laufzeit-Spielereien in einem meiner Repos hier sehen.

Alex Gray
quelle
5

Ich weiß, dass dies schon vor langer Zeit beantwortet wurde, aber ich möchte es trotzdem teilen. Dies kann auch mit erfolgen sel_registerName.

Der Beispielcode in der Frage kann folgendermaßen umgeschrieben werden:

SEL selector = sel_registerName("doWork:");
[myobj respondsToSelector:selector];
Krypton
quelle
2
Eigentlich verwendet NSSelectorFromStringvon @ torsten-marek sel_registerNameunter der Haube. appledev : "NSSelectorFromString übergibt eine UTF-8-codierte Zeichendarstellung von aSelectorName an sel_registerName und gibt den von dieser Funktion zurückgegebenen Wert zurück"
PLG