Domanda

Mi piacerebbe specificare un protocollo di Objective-C con una routine opzionale. Quando la routine non è implementata da una classe conforme al protocollo vorrei usare un'implementazione di default al suo posto. C'è un posto nel protocollo stesso in cui posso definire questa implementazione di default? Se no, qual è la pratica migliore per ridurre copiare e incollare questa implementazione predefinita in tutto il luogo?

È stato utile?

Soluzione

protocolli di Objective-C non hanno alcun affordance per le implementazioni di default. Essi sono puramente collezioni di dichiarazioni di metodi che possono essere implementati da altre classi. La pratica standard in Objective-C è testare un oggetto in fase di esecuzione per vedere se risponde ad un dato selettore prima di chiamare tale metodo su di esso, utilizzando - [NSObject respondsToSelector:]. Se e oggetto non risponde alla data selettore, il metodo non viene chiamato.

Un modo è possibile ottenere il risultato che stai cercando potrebbe essere quella di definire un metodo incapsulare il comportamento predefinito che stai cercando nella classe chiamata, e chiamare tale metodo se l'oggetto non supera il test.

Un altro approccio sarebbe quello di rendere essere richiesto il metodo nel protocollo, e di fornire implementazioni di default nelle superclassi di tutte le classi in cui non si può decidere di fornire un'implementazione specifica.

Ci sono probabilmente anche altre opzioni, ma in linea generale, non v'è una particolare pratica standard in Objective-C, se non forse non solo di chiamare il metodo indicato se non è stato implementare dall'oggetto, per il mio primo comma, sopra.

Altri suggerimenti

Non esiste un modo standard per farlo come protocolli non dovrebbero definire eventuali implementazioni.

Dal Objective-C è dotato di un tempo di esecuzione ordinata, si può naturalmente aggiungere un tale comportamento se davvero pensi che devi fare in questo modo (e non c'è alcuna possibilità per raggiungere lo stesso con l'ereditarietà).

Di che hai dichiarato MyProtocol, poi basta aggiungere un'interfaccia con lo stesso nome del file .h sotto la vostra dichiarazione di protocollo:

@interface MyProtocol : NSObject <MyProtocol>

+ (void)addDefaultImplementationForClass:(Class)conformingClass;

@end

E creare un file di implementazione corrispondente (utilizzando MAObjCRuntime per migliorare la leggibilità qui, ma le funzioni di runtime standard di wouldn' t essere molto più codice):

@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
}

Poi basta alla chiamata

[MyProtocol addDefaultImplementationForClass:[self class]];

nella inizializzazione della classe conforme al protocollo e verrà aggiunto tutti i metodi predefiniti.

Un modo davvero affascinante è quello di utilizzare il runtime. Allo start-up, molto presto l'esecuzione del programma, procedere come segue:

  1. enumerare tutte le classi, trovare le classi che implementano il protocollo
  2. Verificare se gli attrezzi di classe un metodo
  3. In caso contrario, aggiungere alla classe l'implementazione predefinita

Si può essere raggiunto senza che troppi problemi.

Come Ryan parlare non ci sono implementazioni di default per i protocolli, un'altra opzione per attuare nella superclasse sarebbe è di attuare un "Handler" tipo di classe che può essere contenuto in ogni classe che desidera fornire l'implementazione predefinita, l'appropriato metodo quindi chiama l'implementazione gestori predefiniti.

Ho finito per creare una macro che dispone di un'implementazione predefinita del metodo.

Ho definito nel file di intestazione del protocollo, e poi è solo una battuta in ogni implementazione.

In questo modo, io non c'è bisogno di cambiare l'implementazione diversi luoghi, ed è fatto in tempo di compilazione, in modo da nessuna magia di run-time è necessario.

Sono d'accordo con "w.m." Una soluzione molto bello è quello di mettere tutte le implementazioni predefinite in un'interfaccia (con lo stesso nome come il protocollo). Nel metodo "+ initialize" di qualsiasi sottoclasse semplicemente può copiare tutti i metodi non implementati dalla interfaccia predefinita in sé.

Le seguenti funzioni di supporto lavorato per me

#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);
}

Poi si chiama nella classe di inizializzazione come ...

@interface Foobar : NSObject<MyProtocol>  
@end

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

Xcode vi darà avvertimenti circa Foobar mancante metodi, ma li si può ignorare.

Questa tecnica solo metodi copie, non Ivars. Se i metodi accedono membri di dati che non esistono, si potrebbe ottenere strani insetti. È necessario assicurarsi che i dati sono compatibili con il codice. E 'come se hai fatto un reinterpret_cast da Foobar a MyProtocol.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top