Использование -performSelector: вместо простого вызова метода

Ответы:

191

По сути, performSelector позволяет вам динамически определять, какой селектор вызывать селектор для данного объекта. Другими словами, селектор не нужно определять до выполнения.

Таким образом, даже если они эквивалентны:

[anObject aMethod]; 
[anObject performSelector:@selector(aMethod)]; 

Вторая форма позволяет это сделать:

SEL aSelector = findTheAppropriateSelectorForTheCurrentSituation();
[anObject performSelector: aSelector];

перед отправкой сообщения.

ennuikiller
источник
3
Стоит отметить, что вы фактически назначаете результат findTheApp MatchSelectorForTheCurrentSituation () aSelector, а затем вызываете [anObject performSelector: aSelector]. @selector производит SEL.
Daniel Yankowsky
4
Использование performSelector:- это то, что вы, вероятно, будете делать только в том случае, если вы реализуете целевое действие в своем классе. Родные братья performSelectorInBackground:withObject:и performSelectorOnMainThread:withObject:waitUntilDone:часто являются более полезными. Для создания фонового потока и для обратного вызова результатов в основной поток из указанного фонового потока.
PeyloW
2
performSelectorтакже полезно для подавления предупреждений компиляции. Если вы знаете, что метод существует (например, после использования respondsToSelector), он не позволит Xcode сказать «может не отвечать your_selector». Только не используйте его вместо того, чтобы выяснить настоящую причину предупреждения. ;)
Marc
Я читал в другом треде на StackOverflow, что использование performSelector было отражением ужасного дизайна, и у него было много одобрения. Я хотел бы найти его снова. Я искал в Google, ограничивая результаты до stackoverflow, и получил 18 000 результатов. Eww.
Logicsaurus Rex
«отражение ужасного дизайна» слишком упрощенно. Это было то, что у нас было до того, как блоки стали доступны, и не все варианты использования плохи ни тогда, ни сейчас. Хотя в настоящее время , что блоки имеют в наличии, это, вероятно , лучший выбор для нового кода , если вы делаете что - то очень простое.
Итан
16

Для этого очень простого примера в вопросе

[object doSomething];
[object performSelector:@selector(doSomething)]; 

нет никакой разницы в том, что произойдет. doSomething будет синхронно выполняться объектом. Только doSomething - это очень простой метод, который ничего не возвращает и не требует никаких параметров.

было ли это что-то более сложное, например:

(void)doSomethingWithMyAge:(NSUInteger)age;

все станет сложнее, потому что [object doSomethingWithMyAge: 42];

больше нельзя вызывать с любым вариантом "performSelector", потому что все варианты с параметрами принимают только параметры объекта.

Селектор здесь будет "doSomethingWithMyAge:", но любая попытка

[object performSelector:@selector(doSomethingWithMyAge:) withObject:42];  

просто не компилируется. передача NSNumber: @ (42) вместо 42 тоже не поможет, потому что метод ожидает базовый тип C, а не объект.

Кроме того, есть варианты performSelector до 2-х параметров, не более. Хотя методы во много раз имеют гораздо больше параметров.

Я обнаружил, что хотя синхронные варианты performSelector:

- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

всегда возвращать объект, я также смог вернуть простой BOOL или NSUInteger, и это сработало.

Одно из двух основных применений performSelector - динамическое составление имени метода, который вы хотите выполнить, как объяснялось в предыдущем ответе. Например

 SEL method = NSSelectorFromString([NSString stringWithFormat:@"doSomethingWithMy%@:", @"Age");
[object performSelector:method];

Другое использование - асинхронная отправка сообщения объекту, который будет выполнен позже в текущем цикле выполнения. Для этого есть несколько других вариантов performSelector.

- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
- (void)performSelector:(SEL)aSelector target:(id)target argument:(id)arg order:(NSUInteger)order modes:(NSArray *)modes;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg;

(да, я собрал их из нескольких категорий классов Foundation, таких как NSThread, NSRunLoop и NSObject)

Каждый из вариантов имеет свое особое поведение, но все они имеют что-то общее (по крайней мере, когда для параметра waitUntilDone установлено значение NO). Вызов «performSelector» вернется немедленно, а сообщение объекту будет помещено в текущий цикл выполнения только через некоторое время.

Из-за отложенного выполнения - естественно, нет возвращаемого значения из метода селектора, следовательно, возвращаемое значение - (void) во всех этих асинхронных вариантах.

Надеюсь, я как-то прикрыл это ...

Мотти Шнеор
источник
12

@ennuikiller на высоте. По сути, динамически сгенерированные селекторы полезны, когда вы не знаете (и обычно не можете) знать имя метода, который вы будете вызывать при компиляции кода.

Одно ключевое отличие состоит в том, что -performSelector:и друзья (включая многопоточные и отложенные варианты ) несколько ограничены тем, что они предназначены для использования с методами с параметрами 0–2. Например, вызов -outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation:с 6 параметрами и возвращение NSStringдовольно громоздко и не поддерживается предоставленными методами.

Куинн Тейлор
источник
5
Для этого вам нужно будет использовать NSInvocationобъект.
Дэйв Делонг,
6
Еще одно отличие: performSelector:все друзья принимают аргументы объекта, то есть вы не можете использовать их для вызова (например) setAlphaValue:, потому что его аргумент - это число с плавающей запятой.
Чак,
4

Селекторы немного похожи на указатели функций в других языках. Вы используете их, когда во время компиляции не знаете, какой метод вы хотите вызвать во время выполнения. Также, как указатели на функции, они инкапсулируют только глагольную часть вызова. Если у метода есть параметры, их также необходимо передать.

An NSInvocationслужит той же цели, за исключением того, что объединяет больше информации. Он не только включает глагольную часть, но также включает целевой объект и параметры. Это полезно, если вы хотите вызвать метод для определенного объекта с определенными параметрами не сейчас, а в будущем. Вы можете построить соответствующий NSInvocationи запустить его позже.

Даниэль Янковский
источник
5
Селекторы на самом деле совсем не похожи на указатель на функцию в том смысле, что указатель на функцию - это то, что вы можете вызвать с аргументами, а селектор можно использовать для вызова определенного метода для любого объекта, который его реализует; селектор не имеет полного контекста вызова, как указатель на функцию.
bbum
1
Селекторы - это не то же самое, что указатели на функции, но я все же думаю, что они похожи. Они представляют собой глаголы. Указатели функций C также представляют собой глаголы. Ни то, ни другое бесполезно без дополнительного контекста. Селекторы требуют объекта и параметров; указателям на функции требуются параметры (которые могут включать объект, с которым нужно работать). Я хотел подчеркнуть, чем они отличаются от объектов NSInvocation, которые действительно содержат весь необходимый контекст. Возможно, мое сравнение сбило с толку, и в этом случае я прошу прощения.
Даниэль Янковский,
1
Селекторы не являются указателями на функции. Даже не близко. На самом деле это простые строки C, которые содержат «имя» метода (в отличие от «функции»). Они даже не являются сигнатурами методов, потому что не включают типы параметров. У объекта может быть несколько методов для одного и того же селектора (разные типы параметров или разные возвращаемые типы).
Мотти Шнеор 03
-7

Между ними есть еще одно тонкое различие.

    [object doSomething]; // is executed right away

    [object performSelector:@selector(doSomething)]; // gets executed at the next runloop

Вот выдержка из документации Apple

"performSelector: withObject: afterDelay: выполняет указанный селектор в текущем потоке во время следующего цикла цикла выполнения и после необязательного периода задержки. Поскольку он ожидает до следующего цикла цикла выполнения, чтобы выполнить селектор, эти методы обеспечивают автоматическую мини-задержку от код, выполняемый в данный момент. Несколько селекторов в очереди выполняются один за другим в том порядке, в котором они были поставлены в очередь ».

AVI
источник
1
Ваш ответ фактически неверен. Документация, о которой вы цитируете, касается performSelector:withObject:afterDelay:, но вопрос и ваш фрагмент используютperformSelector: , а это совершенно другой метод. Из документации к нему: <quote> performSelector:Метод эквивалентен отправке aSelectorсообщения непосредственно получателю. </quote>
jscs
3
спасибо Джошу за разъяснения. Ты прав; Я думал, что performSelector/performSelector:withObject/performSelector:withObject:afterDelayвсе вели себя одинаково, что было ошибкой.
avi