Usare -performSelector: rispetto alla semplice chiamata del metodo


Risposte:


191

Fondamentalmente performSelector ti permette di determinare dinamicamente quale selettore chiamare un selettore su un dato oggetto. In altre parole, il selettore non deve essere determinato prima del runtime.

Quindi, anche se sono equivalenti:

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

Il secondo modulo ti consente di farlo:

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

prima di inviare il messaggio.


3
Vale la pena sottolineare che assegneresti effettivamente il risultato di findTheAppropriateSelectorForTheCurrentSituation () a aSelector, quindi invocerai [anObject performSelector: aSelector]. @selector produce un SEL.
Daniel Yankowsky,

4
L'utilizzo performSelector:è qualcosa che probabilmente fai solo se implementi l'azione target nella tua classe. I fratelli performSelectorInBackground:withObject:e le sorelle performSelectorOnMainThread:withObject:waitUntilDone:sono spesso più utili. Per generare un thread in background e per richiamare i risultati al thread principale da detto thread in background.
PeyloW,

2
performSelectorè utile anche per sopprimere gli avvisi di compilazione. Se sai che il metodo esiste (come dopo l'uso respondsToSelector), impedirà a Xcode di dire "potrebbe non rispondere a your_selector". Basta non usarlo invece di scoprire la vera causa dell'avvertimento. ;)
Marc

Ho letto su qualche altro thread su StackOverflow che l'uso di performSelector era un riflesso di un design orribile, e aveva un sacco di pollici in su. Vorrei poterlo ritrovare. Ho cercato su google, limitando i risultati a stackoverflow e ho ottenuto 18.000 risultati. Eww.
Logicsaurus Rex

Il "riflesso di un design orribile" è eccessivamente semplicistico. Questo era ciò che avevamo prima che i blocchi fossero disponibili, e non tutti gli usi sono cattivi, allora o adesso. Sebbene ora che i blocchi siano disponibili, questa è probabilmente una scelta migliore per il nuovo codice a meno che tu non stia facendo qualcosa di molto semplice.
Ethan

16

Per questo esempio molto semplice nella domanda,

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

non c'è differenza in ciò che accadrà. doSomething verrà eseguito in modo sincrono dall'oggetto. Solo "doSomething" è un metodo molto semplice, che non restituisce nulla e non richiede alcun parametro.

fosse qualcosa di un po 'più complicato, come:

(void)doSomethingWithMyAge:(NSUInteger)age;

le cose si complicherebbero, perché [object doSomethingWithMyAge: 42];

non può più essere richiamato con nessuna variante di "performSelector", perché tutte le varianti con parametri accettano solo parametri oggetto.

Il selettore qui sarebbe "doSomethingWithMyAge:" ma qualsiasi tentativo di farlo

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

semplicemente non verrà compilato. passare un NSNumber: @ (42) invece di 42, non sarebbe di aiuto, perché il metodo si aspetta un tipo C di base, non un oggetto.

Inoltre, ci sono varianti performSelector fino a 2 parametri, non di più. Mentre molte volte i metodi hanno molti più parametri.

Ho scoperto che sebbene le varianti sincrone di performSelector:

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

restituire sempre un oggetto, sono stato in grado di restituire anche un semplice BOOL o NSUInteger e ha funzionato.

Uno dei due usi principali di performSelector è comporre dinamicamente il nome del metodo che si desidera eseguire, come spiegato in una risposta precedente. Per esempio

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

L'altro uso è inviare in modo asincrono un messaggio all'oggetto, che verrà eseguito in seguito nel ciclo di esecuzione corrente. Per questo, ci sono molte altre varianti di 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;

(sì, li ho raccolti da diverse categorie di classi Foundation, come NSThread, NSRunLoop e NSObject)

Ciascuna delle varianti ha il suo comportamento speciale, ma tutte condividono qualcosa in comune (almeno quando waitUntilDone è impostato su NO). La chiamata "performSelector" tornerà immediatamente e il messaggio all'oggetto verrà inserito nel ciclo di esecuzione corrente solo dopo un po 'di tempo.

A causa dell'esecuzione ritardata - naturalmente non è disponibile alcun valore di ritorno dal metodo del selettore, da qui il valore di ritorno - (void) in tutte queste varianti asincrone.

Spero di averlo coperto in qualche modo ...


12

@ennuikiller è perfetto. Fondamentalmente, i selettori generati dinamicamente sono utili quando non conosci (e di solito non puoi) il nome del metodo che chiamerai quando compili il codice.

Una differenza fondamentale è che -performSelector:e gli amici (comprese le varianti multi-thread e ritardate ) sono piuttosto limitati in quanto sono progettati per l'uso con metodi con parametri 0-2. Ad esempio, chiamare -outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation:con 6 parametri e restituire il NSStringè piuttosto ingombrante e non supportato dai metodi forniti.


5
Per farlo, dovresti usare un NSInvocationoggetto.
Dave DeLong,

6
Un'altra differenza: performSelector:e tutti gli amici accettano argomenti oggetto, il che significa che non puoi usarli per chiamare (ad esempio) setAlphaValue:, perché il suo argomento è un float.
Chuck

4

I selettori sono un po 'come i puntatori a funzione in altre lingue. Li usi quando non sai in fase di compilazione quale metodo vuoi chiamare in fase di esecuzione. Inoltre, come i puntatori a funzione, incapsulano solo la parte verbale dell'invocazione. Se il metodo ha parametri, sarà necessario passare anche loro.

Un ha NSInvocationuno scopo simile, tranne per il fatto che unisce più informazioni. Non solo include la parte del verbo, ma include anche l'oggetto di destinazione ei parametri. Ciò è utile quando si desidera chiamare un metodo su un particolare oggetto con parametri particolari, non ora ma in futuro. Puoi costruirne uno appropriato NSInvocatione attivarlo in seguito.


5
I selettori in realtà non sono affatto come un puntatore a funzione in quanto un puntatore a funzione è qualcosa che puoi chiamare con argomenti e un selettore può essere usato per chiamare un particolare metodo su qualsiasi oggetto che lo implementa; un selettore non ha il contesto completo di invocazione come un puntatore a funzione.
bbum

1
I selettori non sono gli stessi dei puntatori a funzione, ma continuo a pensare che siano simili. Rappresentano i verbi. I puntatori a funzione C rappresentano anche i verbi. Nessuno dei due è utile senza contesto aggiuntivo. I selettori richiedono un oggetto e parametri; i puntatori a funzione richiedono parametri (che potrebbero includere un oggetto su cui operare). Il mio punto era evidenziare come sono diversi dagli oggetti NSInvocation, che contengono tutto il contesto necessario. Forse il mio confronto è stato confuso, nel qual caso mi scuso.
Daniel Yankowsky

1
I selettori non sono puntatori a funzione. Neanche vicino. Sono in realtà semplici stringhe C, che contengono un "nome" di un metodo (al contrario di "funzione"). Non sono nemmeno firme di metodo, perché non incorporano i tipi di parametri. Un oggetto può avere più di un metodo per lo stesso selettore (diversi tipi di parametro o diversi tipi di ritorno).
Motti Shneor

-7

C'è un'altra sottile differenza tra i due.

    [object doSomething]; // is executed right away

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

Ecco l'estratto dalla documentazione di Apple

"performSelector: withObject: afterDelay: esegue il selettore specificato sul thread corrente durante il successivo ciclo di esecuzione del ciclo e dopo un periodo di ritardo opzionale. Poiché attende fino al successivo ciclo di esecuzione del ciclo per eseguire il selettore, questi metodi forniscono un mini ritardo automatico da il codice attualmente in esecuzione. Più selettori in coda vengono eseguiti uno dopo l'altro nell'ordine in cui sono stati messi in coda. "


1
La tua risposta è effettivamente errata. La documentazione che citi riguarda performSelector:withObject:afterDelay:, ma la domanda e il tuo frammento stanno usando performSelector:, che è un metodo completamente diverso. Dai documenti: <quote> Il performSelector:metodo equivale a inviare un aSelectormessaggio direttamente al destinatario. </quote>
jscs

3
grazie Josh per il chiarimento. Hai ragione; Pensavo si performSelector/performSelector:withObject/performSelector:withObject:afterDelaycomportassero tutti allo stesso modo, il che è stato un errore.
avi
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.