objective c - Definición de categorías para protocolos en Objective-C?
protocols category (7)
En Objective-C, puedo agregar métodos a las clases existentes con una categoría, por ejemplo
@interface NSString (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end
También es posible hacer esto con protocolos, es decir, si había un protocolo NSString, algo como:
@interface <NSString> (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end
Quiero hacer esto ya que tengo varias extensiones para NSObject (la clase), usando solo métodos públicos de NSObject, y quiero que esas extensiones también funcionen con los objetos que implementan el protocolo.
Para dar un ejemplo más, ¿qué sucede si quiero escribir un método logDescription que imprime la descripción de un objeto en el registro?
- (void) logDescription {
NSLog(@"%@", [self description]);
}
Por supuesto, puedo agregar este método a NSObject, pero hay otras clases que no heredan de NSObject, donde también me gustaría tener este método, por ejemplo, NSProxy. Como el método solo usa miembros públicos de protocolo, sería mejor agregarlo al protocolo.
Editar: Java 8 ahora tiene esto con "métodos de extensión virtual" en las interfaces: http://cr.openjdk.java.net/~briangoetz/lambda/Defender%20Methods%20v4.pdf . Esto es exactamente lo que me gustaría hacer en Objective-C. No vi esta pregunta ganar tanta atención ...
Saludos, Jochen
Adam Sharp publicó una solución que funcionó para mí.
Incluye 3 pasos:
- Definir los métodos que desea agregar como
@optional
en un protocolo. - Hacer que los objetos que desea extender se ajusten a ese protocolo.
- Copiando esos métodos en esos objetos en tiempo de ejecución.
Consulte el enlace para ver todos los detalles.
Creo que puedes estar mezclando términos aquí y allá. Las extensiones, categorías, protocolos, interfaces y clases son todas cosas diferentes en Objective-C. En The Objective-C 2.0, Apple describe las diferencias muy bien, incluidos los beneficios y desventajas de usar categorías y extensiones.
Si lo piensas, ¿qué es una "Categoría" o "Extensión" en el sentido conceptual? Es una forma de agregar funcionalidad a una clase. En Objective-C, los protocolos están diseñados para no tener implementación. Por lo tanto, ¿cómo agregaría o extendería la implementación de algo que no tiene implementación desde el principio?
No es realmente significativo hacerlo, ya que un protocolo no puede implementar el método. Un protocolo es una forma de declarar que admite algunos métodos. Agregar un método a esta lista fuera del protocolo significa que todas las clases "conformes" declaran accidentalmente el nuevo método aunque no lo implementen. Si alguna clase implementara el protocolo NSObject pero no desciende de NSObject, y luego agregó un método al protocolo, eso rompería la conformidad de la clase.
Sin embargo, puede crear un nuevo protocolo que incluya el anterior con una declaración como @protocol SpecialObject <NSObject>
.
Respuesta corta: No.
Larga respuesta: ¿cómo funcionaría esto? ¿Imagine que puede agregar métodos a los protocolos existentes? ¿Cómo funcionaría esto? Imagine que queríamos agregar otro método a NSCoding, digamos -(NSArray *) codingKeys;
Este método es un método obligatorio que devuelve una matriz de las claves utilizadas para codificar el objeto.
El problema es que hay clases existentes (como, por ejemplo, NSString) que ya implementan NSCoding, pero no implementan nuestro método codingKeys
. ¿Qué debería pasar? ¿Cómo sabría el marco precompilado qué hacer cuando se envía este mensaje obligatorio a una clase que no lo implementa?
Podría decir "podemos agregar la definición de este método a través de una categoría" o "podríamos decir que cualquier método agregado a través de estas categorías de protocolo es explícitamente opcional". Sí, podrías hacer esto y teóricamente solucionar el problema que he descrito anteriormente. Pero si va a hacer eso, también podría hacerlo una categoría en primer lugar, y luego verificar que la clase respondsToSelector:
al respondsToSelector:
antes de invocar el método.
Si bien es cierto que no puede definir categorías para protocolos (y no desea hacerlo, porque no sabe nada sobre el objeto existente), puede definir categorías de tal forma que el código solo se aplique a un objeto de el tipo dado que tiene el protocolo deseado (algo así como la especialización de plantilla parcial de C ++).
El uso principal para algo como esto es cuando desea definir una categoría que depende de una versión personalizada de una clase. (Imagine que tengo subclases UIViewController que se ajustan al protocolo Foo, lo que significa que tienen la propiedad foo, mi código de categoría puede necesitar la propiedad foo, pero no puedo aplicarlo al protocolo Foo, y si simplemente lo aplico para UIViewController, el código no se compilará por defecto, y obligarlo a compilar significa que alguien haciendo una introspección, o simplemente aturdido, podría llamar a su código que depende del protocolo. Un enfoque híbrido podría funcionar así:
@protocol Foo
- (void)fooMethod
@property (retain) NSString *foo;
@end
@implementation UIViewController (FooCategory)
- (void)fooMethod {
if (![self conformsToProtocol:@protocol(Foo)]) {
return;
}
UIViewController<Foo> *me = (UIViewController<Foo>*) self;
// For the rest of the method, use "me" instead of "self"
NSLog(@"My foo property is /"%@/"", me.foo);
}
@end
Con el enfoque híbrido, puede escribir el código solo una vez (por clase que se supone que implementa el protocolo) y asegúrese de que no afectará las instancias de la clase que no se ajusten al protocolo.
La desventaja es que la síntesis / definición de propiedad todavía tiene que suceder en las subclases individuales.
Si ya está escribiendo una categoría, ¿por qué no simplemente agrega la definición del protocolo en el encabezado justo después de la definición de la categoría?
es decir
@interface NSString (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end
@protocol MyExtendedProtocolName <NSString>
//Method declarations go here
@end
De esta forma, cualquier clase que importe el encabezado de categoría también obtendrá la definición del protocolo, y usted puede agregarla a su clase.
@interface MyClass <OriginalProtocol,MyExtendedProtocolName>
Además, tenga cuidado al crear una subclase de NSString, es un clúster y es posible que no siempre obtenga el comportamiento que espera.
extObjC tiene las cosas más Útiles que puedes hacer con Protocolos / Categorías ... primero está @concreteprotocol
...
- Define un "protocolo concreto", que puede proporcionar implementaciones predeterminadas de métodos dentro del protocolo.
- Un bloque
@protocol
debe existir en un archivo de encabezado y un bloque@concreteprotocol
correspondiente en un archivo de implementación. - Cualquier objeto que se declare conforme con este protocolo recibirá sus implementaciones de método, pero solo si ya no existe un método con el mismo nombre.
MyProtocol.h
@protocol MyProtocol
@required - (void)someRequiredMethod;
@optional - (void)someOptionalMethod;
@concrete - (BOOL)isConcrete;
MyProtocol.m
@concreteprotocol(MyProtocol) - (BOOL)isConcrete { return YES; } ...
por lo tanto, declarar un objeto MyDumbObject : NSObject <MyProtocol>
automáticamente devolverá YES
a isConcrete
.
Además, tienen pcategoryinterface(PROTOCOL,CATEGORY)
que "define la interfaz para una categoría llamada CATEGORY en un protocolo PROTOCOL". Las categorías de protocolo contienen métodos que se aplican automáticamente a cualquier clase que se declara conforme con PROTOCOL. "Hay una macro adjunta que también debe usar en su archivo de implementación. Consulte los documentos.
Por último, pero no menos importante / no relacionado directamente con @protocols
encuentra synthesizeAssociation(CLASS, PROPERTY)
, que "sintetiza una propiedad para una clase que utiliza objetos asociados. Esto es principalmente útil para agregar propiedades a una clase dentro de una categoría. declarado con @property
en la interfaz de la clase especificada (o una categoría sobre él), y debe ser del tipo de objeto ".
Muchas de las herramientas de esta biblioteca abren (todo) las cosas que puedes hacer con ObjC ... de la herencia múltiple ... bueno, tu imaginación es el límite.