Nadal jestem trochę nowy w Objective-C i zastanawiam się, jaka jest różnica między następującymi dwoma stwierdzeniami?
[object performSelector:@selector(doSomething)];
[object doSomething];
Nadal jestem trochę nowy w Objective-C i zastanawiam się, jaka jest różnica między następującymi dwoma stwierdzeniami?
[object performSelector:@selector(doSomething)];
[object doSomething];
Odpowiedzi:
Zasadniczo performSelector pozwala dynamicznie określić, który selektor wywoła selektor na danym obiekcie. Innymi słowy, selektor nie musi być określany przed uruchomieniem.
Tak więc, nawet jeśli są one równoważne:
[anObject aMethod];
[anObject performSelector:@selector(aMethod)];
Drugi formularz pozwala to zrobić:
SEL aSelector = findTheAppropriateSelectorForTheCurrentSituation();
[anObject performSelector: aSelector];
zanim wyślesz wiadomość.
performSelector:
jest czymś, co prawdopodobnie robisz tylko wtedy, gdy zaimplementujesz akcję docelową w swojej klasie. Rodzeństwo performSelectorInBackground:withObject:
i performSelectorOnMainThread:withObject:waitUntilDone:
często są bardziej przydatne. Do tworzenia wątku w tle i do wywoływania wyników z wątku głównego z tego wątku w tle.
performSelector
jest również przydatny do pomijania ostrzeżeń kompilacji. Jeśli wiesz, że metoda istnieje (jak po użyciu respondsToSelector
), Xcode przestanie mówić „może nie odpowiadać your_selector
”. Po prostu nie używaj go, zamiast znaleźć prawdziwą przyczynę ostrzeżenia. ;)
W tym bardzo podstawowym przykładzie w pytaniu
[object doSomething];
[object performSelector:@selector(doSomething)];
nie ma różnicy w tym, co się wydarzy. Funkcja doSomething zostanie wykonana synchronicznie przez obiekt. Jedynie „doSomething” jest bardzo prostą metodą, która nic nie zwraca i nie wymaga żadnych parametrów.
czy było to coś bardziej skomplikowanego, na przykład:
(void)doSomethingWithMyAge:(NSUInteger)age;
sprawy by się skomplikowały, ponieważ [obiekt doSomethingWithMyAge: 42];
nie można już wywoływać z żadnym wariantem „performSelector”, ponieważ wszystkie warianty z parametrami akceptują tylko parametry obiektu.
Selektorem w tym miejscu byłoby „doSomethingWithMyAge:”, ale każda próba
[object performSelector:@selector(doSomethingWithMyAge:) withObject:42];
po prostu się nie skompiluje. przekazanie NSNumber: @ (42) zamiast 42 też by nie pomogło, ponieważ metoda oczekuje podstawowego typu C, a nie obiektu.
Ponadto istnieją warianty performSelector do 2 parametrów, nie więcej. Chociaż metody wielokrotnie mają o wiele więcej parametrów.
Dowiedziałem się, że chociaż synchroniczne warianty performSelectora:
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
zawsze zwracał obiekt, mogłem również zwrócić prosty BOOL lub NSUInteger i zadziałało.
Jednym z dwóch głównych zastosowań performSelector jest dynamiczne tworzenie nazwy metody, którą chcesz wykonać, jak wyjaśniono w poprzedniej odpowiedzi. Na przykład
SEL method = NSSelectorFromString([NSString stringWithFormat:@"doSomethingWithMy%@:", @"Age");
[object performSelector:method];
Innym zastosowaniem jest asynchroniczne wysyłanie komunikatu do obiektu, który zostanie wykonany później na bieżącym runloopie. W tym celu istnieje kilka innych wariantów 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;
(tak, zebrałem je z kilku kategorii klas Foundation, takich jak NSThread, NSRunLoop i NSObject)
Każdy z wariantów ma swoje specjalne zachowanie, ale wszystkie mają coś wspólnego (przynajmniej wtedy, gdy parametr waitUntilDone ma wartość NIE). Wywołanie „performSelector” zwróciło by się natychmiast, a wiadomość do obiektu zostanie umieszczona w bieżącym runloop dopiero po pewnym czasie.
Z powodu opóźnionego wykonania - naturalnie żadna wartość zwracana nie jest dostępna z metody selektora, stąd wartość zwracana - (void) we wszystkich tych wariantach asynchronicznych.
Mam nadzieję, że jakoś to opisałem ...
@ennuikiller jest na miejscu. Zasadniczo selektory generowane dynamicznie są przydatne, gdy nie znasz (i zazwyczaj nie możesz) nazwy metody, którą będziesz wywoływać podczas kompilowania kodu.
Jedną kluczową różnicą jest to, że -performSelector:
i przyjaciele (w tym warianty wielowątkowe i opóźnione ) są nieco ograniczone, ponieważ są zaprojektowane do użytku z metodami z parametrami 0-2. Na przykład wywołanie -outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation:
z 6 parametrami i zwrócenie NSString
jest dość nieporęczne i nie jest obsługiwane przez dostarczone metody.
NSInvocation
obiektu.
performSelector:
wszyscy przyjaciele przyjmują argumenty obiektu, co oznacza, że nie można ich używać do wywoływania (na przykład) setAlphaValue:
, ponieważ ich argumentem jest liczba zmiennoprzecinkowa.
Selektory są trochę podobne do wskaźników funkcji w innych językach. Używasz ich, gdy nie wiesz w czasie kompilacji, którą metodę chcesz wywołać w czasie wykonywania. Podobnie jak wskaźniki funkcji, zawierają one tylko część czasownika wywołania. Jeśli metoda ma parametry, musisz je również przekazać.
An NSInvocation
służy podobnemu celowi, z tym wyjątkiem, że wiąże ze sobą więcej informacji. Zawiera nie tylko część czasownika, ale także obiekt docelowy i parametry. Jest to przydatne, gdy chcesz wywołać metodę na określonym obiekcie z określonymi parametrami, nie teraz, ale w przyszłości. Możesz zbudować odpowiedni NSInvocation
i odpalić go później.
Jest jeszcze jedna subtelna różnica między nimi.
[object doSomething]; // is executed right away
[object performSelector:@selector(doSomething)]; // gets executed at the next runloop
Oto fragment dokumentacji Apple
"performSelector: withObject: afterDelay: Wykonuje określony selektor w bieżącym wątku podczas następnego cyklu pętli uruchamiania i po opcjonalnym okresie opóźnienia. Ponieważ czeka na następny cykl pętli uruchamiania, aby wykonać selektor, metody te zapewniają automatyczne mini opóźnienie od aktualnie wykonywany kod. Wiele selektorów w kolejce jest wykonywanych jeden po drugim w kolejności, w jakiej były w kolejce. "
performSelector:withObject:afterDelay:
, ale pytanie i Twój fragment używają performSelector:
, co jest zupełnie inną metodą. Z dokumentacji: <quote> performSelector:
Metoda jest równoważna wysłaniu aSelector
wiadomości bezpośrednio do odbiorcy. </quote>
performSelector/performSelector:withObject/performSelector:withObject:afterDelay
wszyscy zachowywali się w ten sam sposób, co było błędem.