2009-09-29 11 views

Antwort

183

Mit performSelector können Sie dynamisch bestimmen, welcher Selektor einen Selektor für das angegebene Objekt aufrufen soll. Mit anderen Worten, der Selektor muss nicht vor der Laufzeit bestimmt werden.

So obwohl diese sind äquivalent:

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

bevor Sie die Nachricht senden:

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

Die zweite Form Sie dies tun können.

+3

Es lohnt Hinweis darauf, dass Sie tatsächlich das Ergebnis von findTheAppropriateSelectorForTheCurrentSituation(), um aSelector zuweisen, rufen Sie dann [anObject perform: aSelector]. @selector erzeugt ein SEL. –

+4

Die Verwendung von 'performSelector:' ist etwas, was Sie wahrscheinlich nur tun, wenn Sie eine Zielaktion in Ihrer Klasse implementieren. Die Geschwister 'performSelectorInBackground: withObject:' und 'performSelectorOnMainThread: withObject: waitUntilDone:' sind oft nützlicher. Zum Erstellen eines Hintergrundthreads und zum Zurückrufen von Ergebnissen zum Hauptthread von diesem Hintergrundthread. – PeyloW

+1

'performSelector' ist auch nützlich, um Kompilierungswarnungen zu unterdrücken. Wenn Sie wissen, dass die Methode existiert (wie nach der Verwendung von 'reaymToSelector'), wird Xcode davon abhalten zu sagen," kann nicht auf 'your_selector' antworten". Benutze es nicht, anstatt die wahre Ursache der Warnung herauszufinden.;) – Marc

11

@ennuikiller ist genau richtig. Grundsätzlich sind dynamisch generierte Selektoren hilfreich, wenn Sie den Namen der Methode, die Sie beim Kompilieren des Codes aufrufen, nicht kennen (und normalerweise nicht wissen).

Ein wichtiger Unterschied ist, dass -performSelector: und Freunde (einschließlich der multi-threaded and delayed variants) sind etwas begrenzt, dass sie für die Verwendung mit Methoden mit 0-2 Parameter ausgelegt sind. Beispielsweise ist das Aufrufen von -outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation: mit 6 Parametern und das Zurückgeben des NSString ziemlich unhandlich und wird von den bereitgestellten Methoden nicht unterstützt.

+4

Um dies zu tun, müssen Sie ein 'NSInvocation'-Objekt verwenden. –

+6

Ein weiterer Unterschied: 'performSelector:' und Freunde nehmen alle Objektargumente, was bedeutet, dass Sie sie nicht zum Aufruf von 'setAlphaValue:' verwenden können, weil das Argument ein float ist. – Chuck

+0

Beide sind hervorragende Punkte. –

3

Selektoren sind ein bisschen wie Funktionszeiger in anderen Sprachen. Sie verwenden sie, wenn Sie zur Kompilierzeit nicht wissen, welche Methode Sie zur Laufzeit aufrufen wollen. Ebenso wie Funktionszeiger kapseln sie nur den Verbteil des Aufrufs ein. Wenn die Methode Parameter hat, müssen Sie sie auch übergeben.

Ein NSInvocation dient einem ähnlichen Zweck, außer dass es mehr Informationen miteinander verbindet. Es enthält nicht nur den Verbteil, sondern auch das Zielobjekt und die Parameter. Dies ist nützlich, wenn Sie eine Methode für ein bestimmtes Objekt mit bestimmten Parametern aufrufen möchten, nicht jetzt, sondern in der Zukunft. Sie können eine entsprechende NSInvocation bauen und später feuern.

+5

Selektoren sind überhaupt nicht wie ein Funktionszeiger, da ein Funktionszeiger etwas ist, das Sie mit Argumenten aufrufen können, und ein Selektor kann verwendet werden, um eine bestimmte Methode für jedes Objekt aufzurufen, das sie implementiert; Ein Selektor verfügt nicht wie ein Funktionszeiger über den vollständigen Kontext des Aufrufs. – bbum

+1

Selektoren sind nicht die gleichen wie Funktionszeiger, aber ich denke immer noch, dass sie ähnlich sind. Sie repräsentieren Verben. C-Funktionszeiger repräsentieren auch Verben. Weder ist ohne zusätzlichen Kontext nützlich. Selektoren benötigen ein Objekt und Parameter; Funktionszeiger erfordern Parameter (die ein Objekt enthalten können, auf dem sie arbeiten). Ich wollte hervorheben, wie sie sich von NSInvocation-Objekten unterscheiden, die den notwendigen Kontext enthalten. Vielleicht war mein Vergleich verwirrend, und in diesem Fall entschuldige ich mich. –

+1

Selektoren sind keine Funktionszeiger. Nicht annähernd. Sie sind in Wirklichkeit einfache C-Strings, die einen "Namen" einer Methode enthalten (im Gegensatz zu "Funktion"). Sie sind nicht einmal Methodensignaturen, weil sie die Typen von Parametern nicht einbetten. Ein Objekt kann mehr als eine Methode für denselben Selektor haben (unterschiedliche Param-Typen oder unterschiedliche Rückgabetypen). –

-6

Es gibt einen weiteren feinen Unterschied zwischen den beiden.

[object doSomething]; // is executed right away 

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

Hier der Auszug aus Apple-Dokumentation ist

„perform: withobject: afterDelay: Führt den angegebenen Selektor auf dem aktuellen Thread während des Zyklus nächsten Lauf Schleife und nach einer optionalen Verzögerungsperiode Weil es. wartet bis zum nächsten Laufzykluszyklus, um den Selektor auszuführen, diese Verfahren stellen eine automatische Miniverzögerung von dem gerade ausgeführten Code bereit. Mehrere in Warteschlange befindliche Selektoren werden nacheinander in der Reihenfolge ausgeführt, in der sie in die Warteschlange gestellt wurden.

+1

Ihre Antwort ist sachlich falsch. Die von Ihnen zitierte Dokumentation bezieht sich auf 'performSelector: withObject: afterDelay:', aber die Frage und Ihr Snippet verwenden ['performSelector:'] (http://developer.apple.com/library/mac/documentation/Cocoa/Reference/ Foundation/Protokolle/NSObject_Protocol/Reference/NSObject.html # // Apple_ref/doc/uid/20000052-BBCBGHFG), was eine völlig andere Methode ist. Aus der Dokumentation dafür: Die 'performSelector:' Methode entspricht dem Senden einer 'aSelector' Nachricht direkt an den Empfänger.

+2

danke Josh für die Klarstellung. Du hast Recht; Ich dachte 'performSelector/performSelector: withObject/performSelector: withObject: afterDelay' verhielten sich alle auf die gleiche Weise, was ein Fehler war. – avi

+0

Hey avi. Könnten Sie diese Antwort löschen? Sie scheinen zuzustimmen, dass es ein Schocker ist;) – robinCTS

10

Für dieses sehr einfache Beispiel in der Frage, da

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

gibt keinen Unterschied in dem, was passieren wird. doSomething wird synchron per Objekt ausgeführt. Nur "doSomething" ist eine sehr einfache Methode, die nichts zurückgibt und keine Parameter benötigt.

es etwas ein wenig komplizierter, wie:

(void)doSomethingWithMyAge:(NSUInteger)age; 

Dinge wäre kompliziert, weil [Objekt doSomethingWithMyAge: 42];

kann mit keiner Variante von "performSelector" mehr aufgerufen werden, da alle Varianten mit Parametern nur Objektparameter akzeptieren.

Der Wähler hier wäre "doSomethingWithMyAge:" sein, aber jeder Versuch,

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

einfach nicht kompilieren. Die Übergabe einer NSNummer: @ (42) anstelle von 42 würde ebenfalls nicht helfen, da die Methode einen grundlegenden C-Typ erwartet - kein Objekt.

Darüber hinaus gibt es performSelector Varianten bis zu 2 Parameter, nicht mehr. Während Methoden viele Male mehr Parameter haben.

Ich habe herausgefunden, dass, obwohl synchrone Varianten von perform:

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

immer ein Objekt zurückgeben, konnte ich auch ein einfaches BOOL oder NSUInteger zurückzukehren, und es funktionierte.

Eine der zwei Hauptverwendungen von performSelector besteht darin, den Namen der Methode, die Sie ausführen möchten, dynamisch zu erstellen, wie in einer vorherigen Antwort erläutert. Zum Beispiel

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

Die andere Verwendung ist asynchron eine Nachricht versenden zu widersprechen, die später auf dem aktuellen Runloop ausgeführt werden. Dafür gibt es mehrere andere performSelector-Varianten.

- (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; 

(ja, ich sammelte sie aus mehreren Foundation Klassenkategorien, wie NSThread, NSRunLoop und NSObject)

Jede der Varianten sein eigenes spezielles Verhalten, aber alle teilen etwas gemeinsam (zumindest, wenn waitUntilDone ist auf NEIN eingestellt). Der Aufruf "performSelector" würde sofort zurückkehren, und die Nachricht zum Objekt wird erst nach einiger Zeit auf den aktuellen Runloop gesetzt.

Wegen der verzögerten Ausführung - natürlich ist kein Rückgabewert aus der Methode des Selektors verfügbar, daher der Rückgabewert - (void) in allen diesen asynchronen Varianten.

Ich hoffe, dass ich das irgendwie bedeckt ...

Verwandte Themen