Jak mogę dynamicznie utworzyć selektor w czasie wykonywania z Objective-C?

93

Wiem, jak utworzyć plik SELw czasie kompilacji przy użyciu, @selector(MyMethodName:)ale chcę zrobić dynamiczne tworzenie selektora z pliku NSString. Czy to w ogóle możliwe?

Co mogę zrobić:

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

Co chcę zrobić: (pseudo kod, to oczywiście nie działa)

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

Szukałem dokumentacji Apple API, ale nie znalazłem sposobu, który nie opierałby się na @selector(myTarget:)składni czasu kompilacji .

Craigb
źródło

Odpowiedzi:

180

Nie jestem programistą Objective-C, tylko sympatykiem, ale może NSSelectorFromString jest tym, czego potrzebujesz. W Runtime Reference wspomniano o jasności , że można go użyć do konwersji ciągu znaków na selektor.

Torsten Marek
źródło
5
Muszę odświeżyć moje Google-fu. to jest dokładnie to, czego szukałem (lub nie było).
Craigb
Cóż, nadal mam linki latające w moich zakładkach, odkąd przeczytałem dokumenty Objective-C 2.0 kilka dni temu.
Torsten Marek
40

Zgodnie z dokumentacją XCode, twój psuedocode w zasadzie robi to dobrze.

Najbardziej efektywne jest przypisywanie wartości do zmiennych SEL w czasie kompilacji za pomocą dyrektywy @selector (). Jednak w niektórych przypadkach program może potrzebować przekonwertować ciąg znaków na selektor w czasie wykonywania. Można to zrobić za pomocą funkcji NSSelectorFromString:

setWidthHeight = NSSelectorFromString(aBuffer);

Edycja: Bummer, zbyt wolno. : P

Josh Gagnon
źródło
2
NSStringFromSelector(@"doWork")konwertuje to w drugą stronę (po prostu fyi)
bendytree
8
Myślę, że masz na myśli, NSStringFromSelector (@selector (doWork))
jpswain
A co podobno robi ten selektor? Nie powinniśmy określić bloku czy czegoś takiego?
user4951
13

Muszę powiedzieć, że jest to trochę bardziej skomplikowane, niż mogą sugerować odpowiedzi poprzednich respondentów ... jeśli naprawdę chcesz stworzyć selektor ... a nie tylko „zadzwoń do jednego”, który „masz w pobliżu” .. .

Musisz utworzyć wskaźnik funkcji, który będzie wywoływany przez twoją "nową" metodę. Więc dla metody takiej jak [self theMethod:(id)methodArg];, napiszesz ...

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

a następnie musisz wygenerować IMPblok dynamicznie, tym razem przechodząc, „self” SEL, i inne argumenty ...

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

i dodaj go do swojej klasy, wraz z dokładnym podpisem metody dla całego frajera (w tym przypadku "v@:@"void return, object caller, object argument)

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

Możesz zobaczyć kilka dobrych przykładów tego rodzaju sztuczek środowiska uruchomieniowego w jednym z moich repozytoriów tutaj.

Alex Gray
źródło
5

Wiem, że odpowiedź na to pytanie udzielono dawno temu, ale nadal chcę się nią podzielić. Można to zrobić sel_registerNamerównież za pomocą .

Przykładowy kod w pytaniu można przepisać w następujący sposób:

SEL selector = sel_registerName("doWork:");
[myobj respondsToSelector:selector];
Krypton
źródło
2
Właściwie NSSelectorFromStringwspomniane przez @ torsten-marek używa sel_registerNamepod maską. appledev : "NSSelectorFromString przekazuje reprezentację znakową aSelectorName zakodowaną w UTF-8 do sel_registerName i zwraca wartość zwróconą przez tę funkcję"
PLG