Sono ancora un po 'nuovo in Objective-C e mi chiedo qual è la differenza tra le seguenti due affermazioni?
[object performSelector:@selector(doSomething)];
[object doSomething];
Sono ancora un po 'nuovo in Objective-C e mi chiedo qual è la differenza tra le seguenti due affermazioni?
[object performSelector:@selector(doSomething)];
[object doSomething];
Risposte:
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.
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.
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. ;)
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 ...
@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.
NSInvocation
oggetto.
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.
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 NSInvocation
uno 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 NSInvocation
e attivarlo in seguito.
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. "
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 aSelector
messaggio direttamente al destinatario. </quote>
performSelector/performSelector:withObject/performSelector:withObject:afterDelay
comportassero tutti allo stesso modo, il che è stato un errore.