2009-06-15 6 views
3

Gelegentlich ist es sinnvoll, eine Methodenverteilungstabelle einzurichten, sodass der Aufruf von Methode A die Methode AImpl() aufruft, während die Methode A zu einem anderen Zeitpunkt aufgerufen wird, um BImpl() aufzurufen. Wie kann dies in Objective-C für eine Methode getan werden, die automatisch vom System aufgerufen wird (z. B. Delegate-Methoden)?Wie implementieren oder ändern Sie die Methodentabelle in Objective-C?

Zum Beispiel, wenn das System viewDidAppear zum ersten Mal aufruft, möchte ich es ViewAppearFirstTime aufrufen, während Aufruf von ViewDidAppear einen völlig anderen Körper der Methode treffen wird (anstatt If-Else-Check innerhalb des Codes mit booleschen Flag). Ein anderes Beispiel, sagen wir, dass UIView's drawRect sehr oft in einer App aufgerufen wird, wenn das zum ersten Mal aufgerufene drawRect anders als nachfolgende ist, möchte ich keinen if-test einbauen, weil das der Code schwerer ist lesen und die Überprüfung ist auch nach dem ersten Mal nicht notwendig.

+0

Dies ist sehr problematisch für jede Klasse, die kein Singleton ist, da die Nachschlagetabelle nach Klassen und nicht nach Instanzen sortiert ist. Daher wird jedes Swizzling für jede Instanz der Klasse angewendet. – Chuck

+0

Ich stimme mit Chuck überein - Neuzuordnung von Implementierungen in Klassen wird zwangsläufig zu vielen extrem schwierig zu verfolgenden Fehlern führen. Außerdem stimme ich Ihrer Behauptung nicht zu, dass es Code "schwerer zu lesen" macht. Wenn es mit einem der Standardmuster gemacht wird, sollte Code für sogar Junior-Programmierer ziemlich einfach zu lesen sein. Ihre Lösung wird Code viel schwieriger zu grok machen, d. H. "Schwerer zu lesen". –

+0

Können Sie einen Fall beschreiben, in dem der erste drawRect: sich von dem nachfolgenden drawRect unterscheiden sollte: Aufrufe? Wenn Sie Dinge zwischenspeichern, cachen Sie sie in ihren Getter, nicht in drawRect :. Da eine Ansicht ein- und ausgeschaltet werden kann, ist nicht einmal genau klar, was der "erste" Aufruf zu drawRect bedeutet. Erstes Mal für den Unterricht? Zum ersten Mal zum Beispiel? Das erste Mal, seit wir auf den Bildschirm gebracht wurden? Das erste Mal, seit die Ansicht geladen wurde (was mehr als einmal passieren kann) Hast du ein konkretes Beispiel, dass du angreifst? –

Antwort

3

Ich schärfe hier: der besondere Fall, den Sie beschreiben, sollte wahrscheinlich nicht auf diese Weise erfolgen. Es ist fast sicher über-clever und sollte wahrscheinlich in viewDidLoad und viewWillAppear statt zwei Versionen von viewWillAppear getrennt werden. Das heißt, das Problem kommt auf.

Meine bevorzugte Lösung ist, eine SEL zu halten, die auf das zeigt, was ich tun möchte. Zum Beispiel verwende ich diese Art von Technik für eine „nächste Aktion“ nach einer komplizierten asynchronen Aktivität:

if (self.nextActionSelector != NULL) 
{ 
    [self performSelector:self.nextActionSelector]; 
} 

Also für diesen, könnten Sie ein viewWillAppearSelector verwenden, die im Laufe der Zeit geändert werden würde, und viewWillAppear würde rufen Sie einfach an was auch immer es zeigt auf.

Bens Empfehlung von Method Swizzling ist in einigen Fällen nützlich, aber häufiger, wenn Sie versuchen, das Verhalten eines vorhandenen Objekts anstelle von Ihnen selbst zu ändern. Es kann wirklich Debugging-Spaß machen .... OK, nicht wirklich Spaß. Schmerzlich.Sehr schmerzhaft.

Darüber hinaus würde ich -forwardInvocation: betrachten, die verwendet werden kann, um auf Nachrichten zu antworten, die Sie nicht direkt implementieren. Sie können den Aufruf dann neu schreiben, um die Methode aufzurufen, die Sie wirklich wollen. Dies ist jedoch nicht die Art, wie es verwendet werden soll. Es dient zum transparenten Weiterleiten von Anrufen an andere Objekte. Aber zumindest ist das Debuggen nicht so schwierig, da der Debugger dort hingeht, wo Sie es erwarten.

Im Allgemeinen suche ich in diesem Fall statt eines Booleschen nach dem Beweis, dass ich schon einmal ausgeführt wurde. Überprüfen, ob view bereits gesetzt ist, oder ein anderer Wert, der beim ersten Mal initialisiert wird, so dass ich keine spezielle Variable brauche, und ich bin widerstandsfähig gegenüber Dingen, die meinen Zustand löschen (wie das iPhone Dinge bei knappem Speicher ablegt) . Wo immer möglich, mache ich diese Dinge selbst initialisierend in ihren Gettern, anstatt dass eine externe Partei spezielle Erstlogik hat. Die Logik hier einfach zu halten macht die Wartung viel einfacher, und hey, NSInvocation ist verrückt-langsam (ok, Sie würden es nicht bemerken, außer in einer engen Schleife, aber es ist etwa 500x langsamer als ein Methodenaufruf in meinen Tests. Das sagte Ich schlage nicht vor, diese Entscheidung über die Leistung zu treffen, nur die Wartbarkeit.

2

Lesen Sie this, dann setzen Sie Ihre NSInvocation Instanzen in eine NSDictionary.

2

Die Objective-C-Laufzeit ermöglicht es Ihnen, die Zuordnung von einem Selektor zu einer Methodenimplementierung zur Laufzeit zu ändern, und es ist ziemlich glatt. Weitere Informationen finden Sie in diesem Artikel auf CocoaDev: CocoaDev: Method Swizzling. Sie können jede Funktion, für die Sie die vorhandene Zuordnung haben, austauschen, sodass Sie jede Funktion, die in der Obj-c-Laufzeit verwendet werden soll, effektiv ersetzen.

Es ist nicht wirklich etwas, das Sie überall tun sollten - ich würde zuerst NSInvocations versuchen. Da Sie Ihre eigene viewDidAppear-Funktion schreiben und in Ihrer viewAppearFirstTime-Funktion aufrufen können, ist das Swizzling der Methode wahrscheinlich übertrieben.

Viel Glück!

2

Wenn Sie die Methode für alle Instanzen der Klasse ändern möchten, können Sie es über Methode Swizzling tun. Ich habe nie davon gehört, dass es für diesen Zweck verwendet wird, aber Sie können möglicherweise erreichen, was Sie wollen mit class_replaceMethod von innerhalb Ihrer viewDidAppearFirstTime.

Wenn Sie jedoch die Methode der Versand einer einzelnen Instanz ändern möchten, denke ich, die einzige Möglichkeit ist eine bedingte Anweisung in Ihrem viewDidAppear Methode oder ein Redesign zu berücksichtigen, die keine getrennten Verfahren auf der ersten und nachfolgenden Erscheinungen erfordert (von Ihrer Beschreibung, trennen Sie Ihren Code in viewDidLoad und viewWillAppear Funktionen scheint Sinn zu machen).

Verwandte Themen