primary objective-c protocols standards-compliance default-implementation overhead-minimization

objective-c - primary - realm swift



¿Cómo proporciono una implementación predeterminada para un protocolo Objective-C? (6)

Me gustaría especificar un protocolo Objective-C con una rutina opcional. Cuando la rutina no es implementada por una clase conforme al protocolo, me gustaría usar una implementación predeterminada en su lugar. ¿Hay algún lugar en el protocolo en el que pueda definir esta implementación predeterminada? Si no, ¿cuál es la mejor práctica para reducir la copia y el pegado de esta implementación predeterminada en todo el lugar?


Como Ryan menciona, no hay implementaciones predeterminadas para los protocolos, otra opción para implementar en la superclase sería implementar un tipo de clase de "Manejador" que pueda incluirse en cualquier clase que quiera proporcionar la implementación predeterminada, el método apropiado se llama. La implementación de los manejadores por defecto.


Estoy de acuerdo con "wm" Una muy buena solución es poner todas las implementaciones predeterminadas en una interfaz (con el mismo nombre que el protocolo). En el método "+ inicializar" de cualquier subclase, simplemente puede copiar cualquier método no implementado desde la interfaz predeterminada a sí mismo.

Las siguientes funciones de ayuda me funcionaron

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

Entonces lo llamas en tu inicializador de clase como ...

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

Xcode te advertirá sobre los métodos que faltan para Foobar, pero puedes ignorarlos.

Esta técnica solo copia métodos, no ivars. Si los métodos están accediendo a miembros de datos que no existen, podría obtener errores extraños. Debe asegurarse de que los datos sean compatibles con el código. Es como si hicieras un reinterpret_cast de Foobar a MyProtocol.


Los protocolos Objective-C no tienen capacidad para implementaciones predeterminadas. Son puramente colecciones de declaraciones de métodos que pueden ser implementadas por otras clases. La práctica estándar en Objective-C es probar un objeto en tiempo de ejecución para ver si responde al selector dado antes de llamar a ese método, usando - [NSObject respondesToSelector:]. Si el objeto e no responde al selector dado, el método no se llama.

Una forma de lograr el resultado que está buscando sería definir un método que encapsule el comportamiento predeterminado que está buscando en la clase de llamada y llamar a ese método si el objeto no pasa la prueba.

Otro enfoque sería hacer que el método sea requerido en el protocolo y proporcionar implementaciones predeterminadas en las superclases de cualquier clase en las que no desee proporcionar una implementación específica.

Probablemente hay otras opciones también, pero en términos generales no hay una práctica estándar particular en Objective-C, excepto quizás para no llamar al método dado si no ha sido implementado por el objeto, según mi primer párrafo, arriba .


No hay una forma estándar de hacerlo, ya que los protocolos no deben definir ninguna implementación.

Como Objective-C viene con un tiempo de ejecución ordenado, puede agregar tal comportamiento si realmente cree que debe hacerlo de esa manera (y no hay posibilidad de lograr lo mismo con la herencia).

Digamos que declaró MyProtocol, luego simplemente agregue una interfaz con el mismo nombre en el archivo .h bajo su declaración de protocolo:

@interface MyProtocol : NSObject <MyProtocol> + (void)addDefaultImplementationForClass:(Class)conformingClass; @end

Y cree un archivo de implementación correspondiente (usando aquí MAObjCRuntime para MAObjCRuntime lectura, pero las funciones de tiempo de ejecución estándar no serían 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 }

Entonces solo tienes que llamar

[MyProtocol addDefaultImplementationForClass:[self class]];

en el inicializador de su clase conforme al protocolo y se agregarán todos los métodos predeterminados.


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

Lo he definido en el archivo de encabezado del protocolo, y luego es solo una frase en cada implementación.

De esta manera, no tengo que cambiar la implementación en varios lugares, y se realiza en tiempo de compilación, por lo que no es necesaria la magia en tiempo de ejecución.


Una forma verdaderamente fascinante es utilizar el tiempo de ejecución. Al inicio, muy temprano en la ejecución del programa, haga lo siguiente:

  1. Enumera todas las clases, encuentra las clases que implementan el protocolo.
  2. Compruebe si la clase implementa un método
  3. Si no, agregue a la clase la implementación predeterminada

Se puede lograr sin tantos problemas.