Ответы:
По сути, performSelector позволяет вам динамически определять, какой селектор вызывать селектор для данного объекта. Другими словами, селектор не нужно определять до выполнения.
Таким образом, даже если они эквивалентны:
[anObject aMethod];
[anObject performSelector:@selector(aMethod)];
Вторая форма позволяет это сделать:
SEL aSelector = findTheAppropriateSelectorForTheCurrentSituation();
[anObject performSelector: aSelector];
перед отправкой сообщения.
performSelector:
- это то, что вы, вероятно, будете делать только в том случае, если вы реализуете целевое действие в своем классе. Родные братья performSelectorInBackground:withObject:
и performSelectorOnMainThread:withObject:waitUntilDone:
часто являются более полезными. Для создания фонового потока и для обратного вызова результатов в основной поток из указанного фонового потока.
performSelector
также полезно для подавления предупреждений компиляции. Если вы знаете, что метод существует (например, после использования respondsToSelector
), он не позволит Xcode сказать «может не отвечать your_selector
». Только не используйте его вместо того, чтобы выяснить настоящую причину предупреждения. ;)
Для этого очень простого примера в вопросе
[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) во всех этих асинхронных вариантах.
Надеюсь, я как-то прикрыл это ...
@ennuikiller на высоте. По сути, динамически сгенерированные селекторы полезны, когда вы не знаете (и обычно не можете) знать имя метода, который вы будете вызывать при компиляции кода.
Одно ключевое отличие состоит в том, что -performSelector:
и друзья (включая многопоточные и отложенные варианты ) несколько ограничены тем, что они предназначены для использования с методами с параметрами 0–2. Например, вызов -outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation:
с 6 параметрами и возвращение NSString
довольно громоздко и не поддерживается предоставленными методами.
NSInvocation
объект.
performSelector:
все друзья принимают аргументы объекта, то есть вы не можете использовать их для вызова (например) setAlphaValue:
, потому что его аргумент - это число с плавающей запятой.
Селекторы немного похожи на указатели функций в других языках. Вы используете их, когда во время компиляции не знаете, какой метод вы хотите вызвать во время выполнения. Также, как указатели на функции, они инкапсулируют только глагольную часть вызова. Если у метода есть параметры, их также необходимо передать.
An NSInvocation
служит той же цели, за исключением того, что объединяет больше информации. Он не только включает глагольную часть, но также включает целевой объект и параметры. Это полезно, если вы хотите вызвать метод для определенного объекта с определенными параметрами не сейчас, а в будущем. Вы можете построить соответствующий NSInvocation
и запустить его позже.
Между ними есть еще одно тонкое различие.
[object doSomething]; // is executed right away
[object performSelector:@selector(doSomething)]; // gets executed at the next runloop
Вот выдержка из документации Apple
"performSelector: withObject: afterDelay: выполняет указанный селектор в текущем потоке во время следующего цикла цикла выполнения и после необязательного периода задержки. Поскольку он ожидает до следующего цикла цикла выполнения, чтобы выполнить селектор, эти методы обеспечивают автоматическую мини-задержку от код, выполняемый в данный момент. Несколько селекторов в очереди выполняются один за другим в том порядке, в котором они были поставлены в очередь ».
performSelector:withObject:afterDelay:
, но вопрос и ваш фрагмент используютperformSelector:
, а это совершенно другой метод. Из документации к нему: <quote> performSelector:
Метод эквивалентен отправке aSelector
сообщения непосредственно получателю. </quote>
performSelector/performSelector:withObject/performSelector:withObject:afterDelay
все вели себя одинаково, что было ошибкой.