Wie Sie Ihren Code entdeckt haben, rufen Sie nicht die übergeordnete Klasse Methode, wie Sie wollen, aber die man in der aktuellen Klasse, in Endlosschleife führt.
Ihre erste Option mit diesem konfrontiert ist der Code, etwas entlang der Linien von Refactoring:
@implementation MyDerivedClass
{
- (void)copy:(id)sender
{
[super copy:sender];
[self commonCodeAfterSelector:_cmd withObject:sender];
}
- (void)cut:(id)sender
{
[super cut:sender];
[self commonCodeAfterSelector:_cmd withObject:sender];
}
- (void)notifyAndPerformSelector:(SEL)selector withObject:(id)sender
{
//code...
}
}
Wenn dieser Ansatz Ihrer Situation es verwenden, entspricht. Wenn nicht ...
Eine zweite Option ist der Compiler zu werden ...
Standard- und Super-Methode ruft
Ein Standard-Methodenaufruf der Form:
[object someMethodWithArg1:x andArg2:y]
ruft eine Suche für die Methode someMethodWithArg1:andArg2:
auf. Diese Suche beginnt mit der Laufzeit Klasse object
. Die Betonung auf Laufzeit ist wichtig, das tatsächliche Objekt, das von object
referenziert wird, könnte der gleichen Klasse wie der deklarierte Typ object
oder und der Unterklassen dieses Typs sein, und die Suche muss die am weitesten abgeleitete Implementierung der Methode finden.
Ein Super-Methodenaufruf der Form:
[super someMethodWithArg1:x andArg2:y]
ruft auch eine Suche für das Verfahren someMethodWithArg1:andArg2:
. Jedoch in dieser Suche beginnt bei der Kompilierzeit Klasse Superklasse der Klasse, in der Code auftritt. Zum Beispiel, wenn MyDerivedClass
oben ist eine Unterklasse von MyBaseClass
dann die Suche nach dem Verfahren beginnt bei MyBaseClass
der Laufzeittyp von self
ignoriert - was MyDerivedClass
oder eine Unterklasse davon sein könnte (etwa MyDerivedDerivedClass
)
Warum Ihre aktuelle Code rekursiv?
Ihr Anruf:
[super performSelector:selector withObject:sender];
beginnt die Suche nach dem Verfahren performSelector:withObject:
in der übergeordneten Klasse, dass die Suche wird die Methode nicht gefunden, bis sie die NSObject
Klasse erreicht. Sobald die Methode gefunden wird aufgerufen und startet eine Standard (nicht Super) Suche nach dem Verfahren für selector
, beginnt diese Suche bei dem Laufzeittyp von self
und findet so die Methode in MyDerivedClass
... Rekursion.
Was Sie brauchen, ist so etwas wie:
[self performSuperSelector:selector withObject:sender];
aber leider, die nicht existiert. Aber Sie können eine machen ...
Kompilieren Methode ruft
Der Compiler eine Standardmethode Aufruf des Formulars nimmt:
[object someMethodWithArg1:x andArg2:y]
und effektiv (wir polier ein paar Details über werden die Notwendigkeit, diejenigen in unten gefüllt erhalten) kompiliert dies zu einem Aufruf der Laufzeitfunktion objc_msgSend()
:
objc_msgSend(object, @selector("someMethodWithArg1:andArg2:"), x, y)
Beachten Sie, dass der Selektor als SEL
Wert übergeben wird. Hier kommt der Wert für _cmd
her.
Ein super Aufruf der Form:
[super someMethodWithArg1:x andArg2:y]
effektiv zu einem Aufruf an objc_msgSendSuper()
der Form zusammengestellt:
objc_msgSendSuper(`struct` containing `self` and superclass,
@selector("someMethodWithArg1:andArg2:"), x, y)
Sie können in Ihrem eigenen Code direkt auf diese Runtime-Funktionen aufrufen. Sie müssen die <objc/objc-runtime.h>
importieren, um die Definitionen zu erhalten, so dass sie in den entsprechenden Typ gegossen usw.
Becoming der Compiler und unter Umgehung performSelector
Ihr Code verwendet performSelector
, da es einen SEL
Wert hat, aber wie oben gezeigt Die Laufzeitaufrufe für Methodenaufruf nehmen direkt eine SEL
. Wenn Sie den Super-Aufruf selbst kompilieren, müssen Sie performSelector
nicht verwenden, was wiederum das Rekursionsproblem vermeidet.
Vor dem Aufruf objc_msgSendSuper()
muss die Funktion umgewandelt werden, damit die Rückgabe- und Argumenttypen mit den tatsächlichen Rückgabe- und Argumenttypen des Selektors übereinstimmen, den Sie aufrufen. Dies ist so, dass der richtige Code kompiliert wird, um die Argumente und den Rückgabewert zu verarbeiten, und dieser Code hängt von den Typen ab. Die beiden Selektoren, die Sie anrufen, copy:
und cut:
, haben den gleichen Typ, der den Code kürzer macht. Um sicherzustellen, das Gießen einfacher zunächst eine Kurzschrift für die Art definieren wir:
typedef void (*CutOrCopyRunner)(struct objc_super *super, SEL op, id sender);
die CurOrCopyRunner
als Funktion Zeigertyp definiert. Jetzt ist Ihre Methode:
- (void)notifyAndPerformSelector:(SEL)selector withObject:(id)sender
{
// "compile" [super selector:sender];
// first cast objc_msgSendSuper to the correct type by
// casting a function pointer to it (a function name by
// itself, e.g. objc_msgSendSuper, evaluates to a pointer
// to the function)
CutOrCopyRunner msgSender = (CutOrCopyRunner)objc_msgSendSuper;
// now build the first argument struct
struct objc_super superInfo;
superInfo.receiver = self;
superInfo.super_class = MyDerivedClass.class.superclass;
// now execute the super call
msgSender(&superInfo, selector, sender);
// code...
}
HTH
Ich bin daran interessiert, wie _CMD als Wähler passieren, anstatt richtige Extraktion für diesen speziellen Code –