Використання -performSelector: проти просто виклику методу


Відповіді:


191

В основному PerformSelector дозволяє динамічно визначати, яким селектором викликати селектор на даному об'єкті. Іншими словами, селектор не потрібно визначати до часу виконання.

Таким чином, хоча вони є рівнозначними:

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

Друга форма дозволяє зробити це:

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

перш ніж надіслати повідомлення.


3
Варто зазначити, що ви насправді призначите результат findTheAppropriaSelectorForTheCurrentSituation () aSelector, а потім викликте [anObject performSelector: aSelector]. @selector виробляє SEL.
Даніель Янковський

4
Використання performSelector:- це те, що ви, мабуть, робите лише в тому випадку, якщо реалізуєте цільові дії у своєму класі. Братів performSelectorInBackground:withObject:і сестер performSelectorOnMainThread:withObject:waitUntilDone:часто корисніші. Для нерестування фонової нитки та для виклику результатів до головної нитки із зазначеної фонової нитки.
PeyloW

2
performSelectorтакож корисно для придушення компіляційних попереджень. Якщо ви знаєте, що метод існує (як, наприклад, після використання respondsToSelector), він зупинить Xcode від "не може відповісти your_selector". Просто не використовуйте його замість того, щоб з’ясувати справжню причину попередження. ;)
Марк

Я читав на іншій темі в StackOverflow, що використання performSelector було відображенням жахливого дизайну, і він мав тонни великих пальців. Я б хотів, щоб я міг її знову знайти. Я шукав google, обмеживши результати stackoverflow, і отримав 18000 результатів. 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, і він спрацював.

Одне з двох основних напрямків виконанняSelector - динамічно складати назву методу, який потрібно виконати, як пояснено в попередній відповіді. Наприклад

 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 встановлена ​​на НІ). Виклик "performSelector" повернеться негайно, а повідомлення на об'єкт буде розміщено на поточному циклі через деякий час.

Через затримку виконання - природно, значення повернення не доступне у методі селектора, отже, - (недійсне) значення повернення у всіх цих асинхронних варіантах.

Сподіваюся, я якось висвітлював це ...


12

@ennuikiller не ввімкнено. В основному, динамічно згенеровані селектори корисні для тих випадків, коли ви не знаєте (і зазвичай не можете) назви методу, до якого ви будете викликати, коли компілюєте код.

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


5
Для цього вам потрібно буде використовувати NSInvocationоб’єкт.
Дейв ДеЛонг

6
Ще одна відмінність: performSelector:і друзі приймають об'єктивні аргументи, тобто ви не можете використовувати їх для виклику (наприклад) setAlphaValue:, оскільки його аргумент є плаваючою.
Чак

4

Селектори трохи схожі на функціональні вказівники іншими мовами. Ви використовуєте їх, коли під час компіляції не знаєте, який метод потрібно зателефонувати під час виконання. Також, як і покажчики функцій, вони лише інкапсулюють частину дієслова виклику. Якщо у методу є параметри, вам потрібно буде також їх передавати.

Служба NSInvocationслужить подібній меті, за винятком того, що вона пов'язує разом більше інформації. Вона не тільки включає в себе частину дієслова, вона також включає цільовий об'єкт і параметри. Це корисно, коли ви хочете викликати метод на певному об'єкті з певними параметрами не зараз, а в майбутньому. Ви можете побудувати відповідний NSInvocationі звільнити його пізніше.


5
Селектори насправді зовсім не схожі на покажчик функції в тому, що покажчик функції - це те, що можна викликати аргументами, і селектор може використовуватися для виклику певного методу на будь-якому об'єкті, який його реалізує; у селектора немає повного контексту виклику, як указівник функції.
bbum

1
Селектори не є такими, як функціональні вказівники, але я все одно думаю, що вони схожі. Вони являють собою дієслова. Покажчики функції C також представляють дієслова. Жоден з них не корисний без додаткового контексту. Селекторам потрібен об'єкт і параметри; покажчикам функцій потрібні параметри (які можуть включати об'єкт, над яким працювати). Моя думка полягала в тому, щоб підкреслити, чим вони відрізняються від об'єктів NSInvocation, які містять весь необхідний контекст. Можливо, моє порівняння було заплутаним, і в цьому випадку я вибачаюся.
Даніель Янковський

1
Селектори не є функціональними покажчиками. Навіть близько не. Вони насправді є простими рядками C, що містять "ім'я" методу (на відміну від "функції"). Вони навіть не є підписами методів, оскільки вони не вбудовують типи параметрів. Об'єкт може мати більше одного методу для одного і того ж селектора (різні типи парам або різний тип повернення).
Motti Shneor

-7

Є ще одна тонка різниця між ними.

    [object doSomething]; // is executed right away

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

Ось уривок з Apple Documentation

"performSelector: withObject: afterDelay: Виконує вказаний селектор на поточному потоці протягом наступного циклу циклу запуску та після необов'язкового періоду затримки. Оскільки він чекає, поки наступний цикл циклу запуску виконає селектор, ці методи забезпечують автоматичну міні-затримку з код, який виконується в даний час. Кілька селекторів у черзі виконуються один за одним у тому порядку, в якому вони стояли в черзі. "


1
Ваша відповідь фактично неправильна. Документація, яку ви цитуєте, стосується performSelector:withObject:afterDelay:, але питання та ваш фрагмент використовуються performSelector:- це зовсім інший метод. З документів для цього: <quote> performSelector:Метод еквівалентний відправці aSelectorповідомлення безпосередньо одержувачу. </quote>
jscs

3
дякую Джошу за роз’яснення. Ви праві; Я думав, що performSelector/performSelector:withObject/performSelector:withObject:afterDelayвсі поводилися однаково, що було помилкою.
avi
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.