¿Objective-C soporta Mixin como Ruby?
mixins objective-c-2.0 (4)
En Ruby, hay módulos y puedes extender una clase "mezclando" el módulo.
module MyModule
def printone
print "one"
end
end
class MyClass
include MyModule
end
theOne = MyClass.new
theOne.printone
>> one
En Objective-C, encuentro que tengo un conjunto de métodos comunes que quiero que una clase de "herede". ¿Qué otras formas puedo lograr esto sin crear una clase común y derivar todo de esa clase común?
Enchufe desvergonzado: ObjectiveMixin
Aprovecha la capacidad del tiempo de ejecución de Objective-C para agregar métodos a una clase en tiempo de ejecución (a diferencia de las categorías, que solo son de tiempo de compilación). Compruébalo, funciona bastante bien y de manera similar a los mixins de Ruby.
Esta es mi opinión sobre la implementación de Mixins en Objective-C, sin usar el tiempo de ejecución de Objective-C directamente. Tal vez sea útil para alguien: https://.com/a/19661059/171933
Puedes literalmente mezclar el código usando #include. Esto no es aconsejable y está en contra de todas las religiones en object-c, sin embargo, funciona perfectamente.
Por favor, no lo hagas en el código de producción.
por ejemplo en el archivo:
MixinModule.header (no debe compilarse ni copiarse en el destino)
-(void)hello;
MixinModule.body (no debe compilarse ni copiarse en el destino)
-(void)hello{
NSLog(@"Hello");
}
en la clase mixin:
@interface MixinTest : NSObject
#include "MixinModule.header"
@end
@implementation MixinTest
#include "MixinModule.body"
@end
caso de uso:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]){
@autoreleasepool {
[[[MixinTest new] autorelease] hello];
}
return 0;
}
Por favor, no lo hagas en el código de producción.
Edición : cambios agregados porque algunas personas sienten que soy responsable de las limitaciones de Objective-C.
Respuesta corta : no puedes. Objective-C no tiene el equivalente de mixins de Ruby.
Respuesta un poco menos breve : Objective-C tiene algo con posiblemente el mismo sabor: los protocolos. Los Protocolos (Interfaces en otros idiomas), son una forma de definir un conjunto de métodos que una clase que adopta que los protocolos se compromete a implementar. Sin embargo, un protocolo no proporciona una implementación. Esa limitación impide el uso de protocolos como un equivalente exacto a las mezclas Ruby.
Aún menos respuesta corta: Sin embargo, el tiempo de ejecución de Objective-C tiene una API expuesta que le permite jugar con las características dinámicas del lenguaje. Luego, se sale del idioma, pero puede tener protocolos con implementaciones predeterminadas (también llamados protocolos concretos). La respuesta de Vladimir muestra una forma de hacerlo. En ese momento me parece que tienes Ruby mixins bien.
Sin embargo, no estoy seguro de que recomendaría hacerlo. En la mayoría de los casos, otros patrones se ajustan a la cuenta sin jugar juegos con el tiempo de ejecución. Por ejemplo, puede tener un subobjeto que implemente el método de mezcla ( has-a en lugar de is-a ). Jugar con el tiempo de ejecución está bien, pero tiene 2 inconvenientes:
Usted hace que su código sea menos legible, ya que requiere que los lectores sepan mucho más que el idioma. Seguro que puedes (y deberías) comentarlo, pero recuerda que cualquier comentario necesario puede verse como un defecto de implementación.
Dependes de esa implementación del lenguaje. Claro, las plataformas de Apple son, con mucho, las más comunes para Objective-C, pero no olvide Cocotron o GnuStep (o Etoilé) que tienen diferentes tiempos de ejecución, que pueden o no ser compatibles con los de Apple en ese aspecto.
Como nota al margen, declaro a continuación que las categorías no pueden agregar estado (variables de instancia) a una clase. Al utilizar la API de tiempo de ejecución, también puede levantar esa limitación. Sin embargo, esto está fuera del alcance de esta respuesta.
Respuesta larga:
Dos características de Objective-C parecen posibles candidatos: categorías y protocolos. Las categorías no son realmente la opción correcta aquí, si entiendo la pregunta correctamente. La característica correcta es un protocolo.
Déjame dar un ejemplo. Supongamos que quieres que un montón de tus clases tengan una habilidad específica llamada "cantar". A continuación, se define un protocolo:
@protocol Singer
- (void) sing;
@end
Ahora puede declarar que cualquiera de sus propias clases adopta el protocolo de la siguiente manera:
@interface Rectangle : Shape <Singer> {
<snip>
@end
@interface Car : Vehicle <Singer> {
<snip>
@end
Al declarar que adoptan el protocolo, se comprometen a implementar el método sing
. Por ejemplo:
@implementation Rectangle
- (void) sing {
[self flashInBrightColors];
}
@end
@implementation Car
- (void) sing {
[self honk];
}
@end
Luego usas esas clases por ejemplo como esto:
void choral(NSArray *choir) // the choir holds any kind of singer
{
id<Singer> aSinger;
for (aSinger in choir) {
[aSinger sing];
}
}
Tenga en cuenta que los cantantes de la matriz no necesitan tener una superclase común. Observe también que una clase puede tener solo una superclase, pero muchos protocolos adoptados. Por último, observe que el compilador realiza la comprobación de tipos.
En efecto, el mecanismo del protocolo es la herencia múltiple utilizada para el patrón de mezcla. Esa herencia múltiple está severamente limitada porque un protocolo no puede agregar nuevas variables de instancia a una clase. Un protocolo solo describe una interfaz pública que los adoptantes deben implementar. A diferencia de los módulos de Ruby, no contiene una implementación.
Eso es lo más de todo. Mencionemos categorías sin embargo.
Una categoría se declara no entre paréntesis angulares, sino entre paréntesis. La diferencia es que se puede definir una categoría para una clase existente para expandirla sin subclasificarla. Incluso puedes hacerlo para una clase de sistema. Como puede imaginar, es posible usar categorías para implementar algo similar a la mezcla. Y se usaron de esa manera durante mucho tiempo, usualmente como categoría para NSObject
(la raíz típica de la jerarquía de herencia), hasta tal punto que fueron llamados protocolos "informales".
Es informal porque el compilador no realiza ninguna comprobación de tipos, y la implementación de los métodos de protocolo es opcional.
Hoy no es necesario usar categorías como protocolos, especialmente porque los protocolos formales ahora pueden declarar que algunos de sus métodos son opcionales con la palabra clave @optional
o requerido (el valor predeterminado) con @required
.
Las categorías siguen siendo útiles para agregar algún comportamiento específico de dominio a una clase existente. NSString
es un objetivo común para eso.
También es interesante señalar que la mayoría (si no todas) de NSObject
instalaciones de NSObject
están de hecho declaradas en un protocolo NSObject
. Esto significa que no es realmente convincente usar NSObject
como una superclase común para todas las clases, aunque esto todavía se hace comúnmente por razones históricas, y bueno ... porque no hay inconveniente para hacerlo. Pero algunas clases de sistema, como NSProxy
, no son NSObject
.