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

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

[object performSelector:@selector(doSomething)]; 

[object doSomething];

Ответ 1

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

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

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

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

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

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

Ответ 2

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

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

Ответ 3

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

[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, а не объект.

Кроме того, существуют варианты исполнения Slector до 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, и он сработал.

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

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

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

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

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

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

Ответ 4

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

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

Ответ 5

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

    [object doSomething]; // is executed right away

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

Вот выдержка из Apple Documentation

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