Как я могу динамически создать селектор во время выполнения с помощью Objective-C?

93

Я знаю, как создать SELво время компиляции, используя, @selector(MyMethodName:)но я хочу создать селектор динамически из файла NSString. Это вообще возможно?

Что я могу сделать:

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

Что я хочу сделать: (псевдокод, это явно не работает)

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

Я искал документы Apple API, но не нашел способа, который бы не полагался на @selector(myTarget:)синтаксис времени компиляции .

Craigb
источник

Ответы:

180

Я не программист на Objective-C, просто сочувствую, но, возможно, NSSelectorFromString - это то, что вам нужно. В Справочнике по среде выполнения упоминается явное указание на то, что вы можете использовать его для преобразования строки в селектор.

Торстен Марек
источник
5
Мне нужно освежить свой гугл-фу. это именно то, что я искал (или, возможно, не искал).
Craigb
Что ж, у меня все еще летают ссылки в моих закладках с тех пор, как я прочитал документы Objective-C 2.0 пару дней назад.
Торстен Марек,
40

Согласно документации XCode, ваш псевдокод в основном понимает это правильно.

Наиболее эффективно назначать значения переменным SEL во время компиляции с помощью директивы @selector (). Однако в некоторых случаях программе может потребоваться преобразовать строку символов в селектор во время выполнения. Это можно сделать с помощью функции NSSelectorFromString:

setWidthHeight = NSSelectorFromString(aBuffer);

Изменить: облом, слишком медленно. :П

Джош Ганьон
источник
2
NSStringFromSelector(@"doWork")преобразует это в другую сторону (просто fyi)
bendytree
8
Я думаю, вы имеете в виду NSStringFromSelector (@selector (doWork))
jpswain
И что якобы делает этот селектор? Разве мы не должны указать блок или что-то в этом роде?
user4951 04
13

Я должен сказать, что это немного сложнее, чем можно было бы предположить из ответов предыдущих респондентов ... если вы действительно действительно хотите создать селектор ... а не просто "вызвать тот", который у вас "валяется" .. .

Вам нужно создать указатель на функцию, который будет вызываться вашим "новым" методом ... так что для такого метода [self theMethod:(id)methodArg];вы должны написать ...

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

а затем вам нужно сгенерировать IMPблок динамически, на этот раз передавая "self" SEL, и любые аргументы ...

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

и добавьте его в свой класс вместе с точной сигнатурой метода для всего присоски (в данном случае "v@:@"void return, вызывающий объект, аргумент объекта)

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

Вы можете увидеть несколько хороших примеров такого рода махинаций во время выполнения в одном из моих репозиториев здесь.

Алекс Грей
источник
5

Я знаю, что на это давным-давно ответили, но все же хочу поделиться. Это можно сделать и с помощью sel_registerName.

Пример кода в вопросе можно переписать следующим образом:

SEL selector = sel_registerName("doWork:");
[myobj respondsToSelector:selector];
Криптон
источник
2
Собственно, NSSelectorFromStringупомянутое @ torsten-marek использует sel_registerNameпод капотом. appledev : «NSSelectorFromString передает закодированное в UTF-8 символьное представление aSelectorName в sel_registerName и возвращает значение, возвращаемое этой функцией»
PLG