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

Я все еще новичок в Objective-C, и мне интересно, в чем разница между следующими двумя утверждениями?

[object performSelector:@selector(doSomething)]; 

[object doSomething];

5 ответов

Решение

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

Таким образом, хотя они эквивалентны:

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

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

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

прежде чем отправить сообщение.

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

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

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

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

(void)doSomethingWithMyAge:(NSUInteger)age;

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

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

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

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

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

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

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

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

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

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

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

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

- (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). Вызов "executeSelector" будет немедленно возвращен, и сообщение объекту будет помещено в текущий цикл выполнения только через некоторое время.

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

Я надеюсь, что я как-то освещал это...

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

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

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

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

Есть еще одна тонкая разница между ними.

    [object doSomething]; // is executed right away

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

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

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

Другие вопросы по тегам