2017-11-14 1 views
0

Ich habe zwei NSResponder-Methoden (Ausschneiden, Kopieren) und sie haben im Grunde den gleichen Code, außer sie nennen ihre eigenen Super. Wie erstelle ich eine Methode mit dem Parameter _CMD als Selektor, die Super aufruft und ich nicht mit Rekursion enden werde?Refactor Duplicity in Code, der Super ruft

- (void)copy:(id)sender 
{ 
    [self notifyAndPerformSelector:_cmd withObject:sender]; 
} 

- (void)cut:(id)sender 
{ 
    [self notifyAndPerformSelector:_cmd withObject:sender]; 
} 

- (void)notifyAndPerformSelector:(SEL)selector withObject:(id)sender 
{ 
    [super performSelector:selector withObject:sender]; 
    //code... 
} 
+0

Ich bin daran interessiert, wie _CMD als Wähler passieren, anstatt richtige Extraktion für diesen speziellen Code –

Antwort

1

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 objectoder 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