Pregunta

Me gustaría especificar un protocolo de Objective-C con una rutina opcional. Cuando la rutina no está implementada por una clase conforme al protocolo que me gustaría usar una implementación por defecto en su lugar. ¿Hay un lugar en el propio protocolo en el que puedo definir esta aplicación por defecto? Si no es así, ¿cuál es la mejor práctica para reducir copiar y pegar esta aplicación por defecto por todo el lugar?

¿Fue útil?

Solución

protocolos de Objective-C no tienen potencialidad para implementaciones por defecto. Ellos son puramente colecciones de declaraciones de métodos que pueden ser implementadas por otras clases. La práctica estándar en Objective-C es poner a prueba un objeto en tiempo de ejecución para ver si responde al selector dado antes de llamar a ese método en él, usando - [NSObject respondsToSelector:]. Si el correo de objetos no responde al selector dado, el método no se llama.

Una forma de poder lograr el resultado que busca sería definir un método de encapsular el comportamiento por defecto que usted está buscando en la categoría de llamar y llamar a dicho método, si el objeto no pasa la prueba.

Otro enfoque sería hacer que se requiere el método en el protocolo, y proporcionar implementaciones predeterminadas en las superclases de ninguna clase en la que es posible que no desee proporcionar una implementación específica.

Probablemente hay otras opciones también, pero en general no es una práctica estándar en particular en Objective-C, excepto tal vez para no llamar al método dado si no ha sido implementar por el objeto, por mi primera párrafo, arriba.

Otros consejos

No hay forma estándar para hacer que los protocolos no deben definir las implementaciones.

Desde Objective-C viene con un tiempo de ejecución ordenada, que por supuesto puede añadir un comportamiento tal si realmente piensa que necesita hacerlo de esa manera (y no hay posibilidad de lograr lo mismo con la herencia).

Diga usted declaró MyProtocol, a continuación, sólo tiene que añadir una interfaz con el mismo nombre en el archivo .h debajo de su declaración de protocolo:

@interface MyProtocol : NSObject <MyProtocol>

+ (void)addDefaultImplementationForClass:(Class)conformingClass;

@end

Y crear un archivo de aplicación correspondiente (usando MAObjCRuntime para facilitar la lectura aquí, pero las funciones de tiempo de ejecución estándar wouldn' t ser mucho más código):

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

A continuación, sólo tiene que llamar

[MyProtocol addDefaultImplementationForClass:[self class]];

en el inicializador de la clase conforme al protocolo y se añadirán todos los métodos por defecto.

Una manera verdaderamente fascinante es utilizar el tiempo de ejecución. En la puesta en marcha, muy temprano en la ejecución del programa, haga lo siguiente:

  1. enumerar todas las clases, encontrar clases que implementan el protocolo
  2. Compruebe si las implementa la clase un método
  3. Si no es así, añadir a la clase de la aplicación por defecto

Se puede lograr sin tantos problemas.

Como Ryan mencionar que no hay implementaciones de protocolos predeterminados, otra opción para la implementación de la superclase sería es implementar un "manejador" tipo de clase que puede estar contenida en cualquier clase que desea proporcionar la implementación predeterminada, la adecuada método entonces llama a la aplicación controladores predeterminados.

terminé creando una macro que tiene una implementación predeterminada del método.

he definido en el archivo de cabecera del protocolo, y luego es sólo una sola línea en cada aplicación.

De esta manera, no tiene que cambiar la implementación de varios lugares, y se hace en tiempo de compilación, por lo que no hay magia en tiempo de ejecución es necesario.

Estoy de acuerdo con "W. M." Una muy buena solución es poner todas las implementaciones por defecto en una interfaz (con el mismo nombre que el protocolo). En el método de "+ initialize" de cualquier subclase puede simplemente copiar cualquiera de los métodos no implementados desde la interfaz por defecto en sí mismo.

Las siguientes funciones de ayudante trabajado para mí

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

A continuación, lo haga en su clase de inicialización como ...

@interface Foobar : NSObject<MyProtocol>  
@end

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

Xcode le dará advertencias acerca Foobar métodos que faltan, pero se puede ignorar.

Esta técnica sólo copia los métodos, no Ivars. Si los métodos están accediendo a los miembros de datos que no existen, se puede obtener errores extraños. Debe asegurarse de que los datos son compatibles con el código. Es como si lo hiciera un reinterpret_cast de Foobar a MyProtocol.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top