Frage

Ich mag ein Objective-C-Protokoll mit einer optionalen Routine spezifizieren. Wenn die Routine von einer Klasse nicht implementiert wird das Protokoll entspricht würde Ich mag eine Standardimplementierung an seiner Stelle verwenden. Gibt es einen Ort in dem Protokoll selbst, wo ich diese Default-Implementierung definieren? Wenn nicht, was ist die beste Praxis zu reduzieren Kopieren und Einfügen dieser Standardimplementierung des ganzen Linie?

War es hilfreich?

Lösung

Objective-C-Protokolle haben keine affordance für Standardimplementierungen. Sie sind rein Sammlungen von Methodendeklarationen, die von anderen Klassen implementiert werden können. Die gängige Praxis in Objective-C ist ein Objekt zur Laufzeit zu testen, um zu sehen, ob es auf den gegebenen Wähler reagiert, bevor diese Methode fordert sie auf, mit - [NSObject respondsToSelector:]. Wenn e-Objekt an den angegebenen Selektor nicht reagiert, wird die Methode nicht aufgerufen.

Eine Möglichkeit, das Ergebnis erzielen könnten Sie suchen wäre, eine Methode zu definieren, um das Standardverhalten Einkapseln Sie suchen in der anrufenden Klasse und rufen Sie diese Methode, wenn das Objekt nicht den Test nicht besteht.

Ein weiterer Ansatz wäre, das Verfahren in dem Protokoll erforderlich machen wird, und Standardimplementierungen in den Oberklassen aller Klassen zu schaffen, bei dem Sie nicht einer speziellen Implementierung zur Verfügung stellen möchten können.

Es gibt wahrscheinlich auch andere Optionen, wie gut, aber es im Allgemeinen nicht eine bestimmte gängige Praxis in Objective-C, außer vielleicht nur nicht die angegebene Methode aufrufe, wenn sie nicht durch das Objekt implementieren haben, pro meinem ersten Absatz oben.

Andere Tipps

Es gibt keine Standardmethode dafür, dass als Protokolle sollten keine Implementierungen definieren.

Da Objective-C mit einer ordentlichen Laufzeit kommt, können Sie selbstverständlich ein solches Verhalten hinzufügen, wenn Sie Sie wirklich denken müssen, es so tun (und es gibt keine Möglichkeit, durch das gleiche mit Vererbung zu erreichen).

Sagen Sie MyProtocol erklärte, dann fügen Sie einfach eine Schnittstelle mit dem gleichen Namen in der H-Datei unter dem Protokoll-Deklaration:

@interface MyProtocol : NSObject <MyProtocol>

+ (void)addDefaultImplementationForClass:(Class)conformingClass;

@end

und erstellen Sie eine entsprechende Implementierungsdatei (mit MAObjCRuntime hier zur besseren Lesbarkeit, aber die Standard-Runtime-Funktionen wouldn‘ t viel mehr Code-):

@implementation MyProtocol

+ (void)addDefaultImplementationForClass:(Class)conformingClass {
  RTProtocol *protocol = [RTProtocol protocolWithName:@"MyProtocol"];
  // get all optional instance methods
  NSArray *optionalMethods = [protocol methodsRequired:NO instance:YES];
  for (RTMethod *method in optionalMethods) {
    if (![conformingClass rt_methodForSelector:[method selector]]) {
      RTMethod *myMethod = [self rt_methodForSelector:[method selector]];
      // add the default implementation from this class
      [conformingClass rt_addMethod:myMethod];
    }
  }
}

- (void)someOptionalProtocolMethod {
  // default implementation
  // will be added to any class that calls addDefault...: on itself
}

Dazu einfach auf Anruf haben

[MyProtocol addDefaultImplementationForClass:[self class]];

in der Initialisierungsliste der Klasse mit dem Protokoll entspricht und alle Standard-Methoden hinzugefügt werden.

Eine wirklich faszinierende Art und Weise ist, um die Laufzeit zu verwenden. Bei der Inbetriebnahme, sehr früh in der Programmausführung, gehen Sie wie folgt:

  1. Aufzählen alle Klassen finden Klassen, die das Protokoll
  2. implementieren
  3. Überprüfen Sie, ob die Klasse implementiert ein Verfahren
  4. Wenn nicht, fügen Sie die Standardimplementierung der Klasse

Es kann, ohne dass viel Mühe erreicht werden.

Als Ryan erwähnt gibt es keine Standardimplementierungen für Protokolle, eine weitere Option in der übergeordneten Klasse umzusetzen wäre ist ein „Handler“ eine Klasse zu implementieren, die in jeder Klasse enthalten sein können, die die Standardimplementierung, die entsprechende zur Verfügung stellen möchten Methode ruft dann die Standard-Handler-Implementierung.

beenden ich ein Makro erstellen auf, die eine Standard-Implementierung des Verfahrens hat.

Ich habe es definiert in dem Header-Datei Protokoll, und es ist dann nur ein Einzeiler in jeder Implementierung.

Auf diese Weise, ich habe nicht die Implementierung an mehreren Stellen zu ändern, und es ist auf der Kompilierung durchgeführt, so dass keine Laufzeit Magie notwendig ist.

Ich stimme mit „W. M.“ Eine sehr schöne Lösung ist es, alle Standardimplementierungen in eine Schnittstelle zu setzen (mit dem gleichen Namen wie das Protokoll). In der „+ initialize“ -Methode jeder Unterklasse kopieren kann sie einfach alle nicht implementierten Methoden aus der Standard-Schnittstelle in sich selbst.

Die folgenden Hilfsfunktionen für mich gearbeitet

#import <objc/runtime.h>

// Get the type string of a method, such as "v@:".
// Caller must allocate sufficent space. Result is null terminated.
void getMethodTypes(Method method, char*result, int maxResultLen)
{
    method_getReturnType(method, result, maxResultLen - 1);
    int na = method_getNumberOfArguments(method);
    for (int i = 0; i < na; ++i)
    {
        unsigned long x = strlen(result);
        method_getArgumentType(method, i, result + x, maxResultLen - 1 - x);
    }
}

// This copies all the instance methods from one class to another
// that are not already defined in the destination class.
void copyMissingMethods(Class fromClass, Class toClass)
{
    // This gets the INSTANCE methods only
    unsigned int numMethods;
    Method* methodList = class_copyMethodList(fromClass, &numMethods);
    for (int i = 0; i < numMethods; ++i)
    {
        Method method = methodList[i];
        SEL selector = method_getName(method);
        char methodTypes[50];
        getMethodTypes(method, methodTypes, sizeof methodTypes);

        if (![toClass respondsToSelector:selector])
        {
            IMP methodImplementation = class_getMethodImplementation(fromClass, selector);
            class_addMethod(toClass, selector, methodImplementation, methodTypes);
        }
    }
    free(methodList);
}

Dann rufen Sie es in Ihrem Klasseninitialisierer wie ...

@interface Foobar : NSObject<MyProtocol>  
@end

@implementation Foobar
+(void)initialize
{
    // Copy methods from the default
    copyMissingMethods([MyProtocol class], self);
}
@end

Xcode finden Sie Warnungen geben über Foobar Methoden fehlt, aber man kann sie ignorieren.

Diese Technik nur Kopien Methoden, Ivars nicht. Wenn die Methoden Datenelemente zugreifen, die nicht existieren, könnten Sie seltsame Fehler erhalten. Sie müssen sicherstellen, dass die Daten mit dem Code kompatibel sind. Es ist, als ob Sie eine reinterpret_cast von Foobar zu MyProtocol hat.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top