2012-04-04 7 views
25

Entonces, hoy estaba aburrido y decidí meterme con la interpolación de C++/Obj-C, y encontré la manera de crear una configuración muy interesante.Objective-C - Desventajas de la creación de puentes con C++?

@protocol NSCPPObj <NSObject> 

-(id) init; 
-(id) initWithInt:(int) value; 
-(int) somethingThatReturnsAValue; 
-(void) doSomething; 

@end 

class NSCPPObj : objc_object { 
public:  
    static Class cls(); 

    int iVar; 

    NSCPPObj(); 
    NSCPPObj(int); 

    int somethingThatReturnsAValue(); 
    void doSomething(); 
}; 

Como puede ver, la interfaz es bastante sencilla y fácil de entender. Creamos dos interfaces (casi) idénticas, una para un objeto C++ y otra para un protocolo Obj-C.

Ahora, he encontrado una manera de implementar esto, pero prepárate, esto se pone feo:

// NSCPPObj.mm 
#import <objc/runtime.h> 
#import <iostream> 

#import "NSCPPObject.h" 

Class NSCPPObj_class = nil; 

__attribute__((constructor)) 
static void initialize() 
{ 
    NSCPPObj_class = objc_allocateClassPair([NSObject class], "NSCPPObj", 0); 

    class_addMethod(NSCPPObj_class->isa, @selector(alloc), imp_implementationWithBlock(^(id self) { 
     return class_createInstance(NSCPPObj_class, sizeof(struct NSCPPObj)); 
    }), "@@:"); 

    class_addMethod(NSCPPObj_class, @selector(init), imp_implementationWithBlock(^(id self) { 
     return self;   
    }), "@@:"); 

    class_addMethod(NSCPPObj_class, @selector(initWithInt:), imp_implementationWithBlock(^(id self, int value) { 
     ((struct NSCPPObj *) self)->iVar = value; 

     return self; 
    }), "@@:i"); 

    class_addMethod(NSCPPObj_class, @selector(doSomething), imp_implementationWithBlock(^(id self) { 
     ((struct NSCPPObj *) self)->doSomething(); 
    }), "[email protected]:"); 
    class_addMethod(NSCPPObj_class, @selector(somethingThatReturnsAValue), imp_implementationWithBlock(^(id self) { 
     return ((struct NSCPPObj *) self)->somethingThatReturnsAValue(); 
    }), "[email protected]:"); 

    objc_registerClassPair(NSCPPObj_class); 
} 

Class NSCPPObj::cls() 
{ 
    return NSCPPObj_class; 
} 

NSCPPObj::NSCPPObj() 
{ 
    this->isa = NSCPPObj_class; 
    [((id<NSCPPObj>) this) init]; 
} 

NSCPPObj::NSCPPObj(int value) 
{ 
    this->isa = NSCPPObj_class; 
    [((id<NSCPPObj>) this) initWithInt:value]; 
} 

void NSCPPObj::doSomething() 
{ 
    std::cout << "Value Is: " << [((id<NSCPPObj>) this) somethingThatReturnsAValue] << std::endl; 
} 

int NSCPPObj::somethingThatReturnsAValue() 
{ 
    return iVar; 
} 

voy a resumir lo que esto hace:

  1. asigna un par de Clase
  2. Agrega todos los métodos de clase e instancia al objeto
  3. Registra la clase Pair

Ahora, como se puede ver, esto no es muy flexible, pero funciona, y es un camino de dos vías:

id<NSCPPObj> obj = [[NSCPPObj::cls() alloc] initWithInt:15]; 
[obj doSomething]; 

NSLog(@"%i", [obj somethingThatReturnsAValue]); 
NSLog(@"%@", obj); 

NSCPPObj *objAsCPP = (__bridge NSCPPObj *) obj; 

objAsCPP->doSomething(); 
std::cout << objAsCPP->somethingThatReturnsAValue() << std::endl; 

También puede crear el objeto utilizando new NSCPPObj(15), pero recuerde que debe ¡bórralo! Obviamente, esto puede funcionar en un entorno ARC o que no sea ARC, pero ARC requiere algunos moldes puente adicionales.

Por lo tanto, llegué a la verdadera pregunta:
¿Cuáles son los pros/contras de esta estructura de diseño? Puedo enumerar algunos fuera de la parte superior de mi cabeza:

Pros:

  1. sobrecarga de operadores con C++
  2. método de Enlace dinámico con ObjC
  3. se puede construir en cualquiera de C++ o la moda ObjC

Contras:

  1. duro de leer aplicación
  2. selectores & fijaciones deben ser agregados para cada aplicación C++ añade a la interfaz
  3. objeto clase no se puede hacer referencia directamente

Así que, después de todo eso, ¿Recomendado estructura de diseño en una aplicación? y por qué.

+1

Sé muy poco de C++ para dar una buena respuesta, pero me pregunto si la respuesta a esto depende de en qué tipo de aplicación estás trabajando exactamente. Un juego existente de C++ que se transfiere puede encontrar esto mucho más útil que una simple aplicación de utilidad ... y un programador de C++ experimentado puede apreciarlo más que alguien que está fuertemente orientado a Objective-C. – lxt

+1

Votación para reabrir. Entiendo que esto puede ser "no constructivo", pero este es un sitio de la comunidad. Reconozco que eres un moderador, pero ya que eres el único que quería que esto se cerrara. –

+0

No soy moderador y marqué esta pregunta para su revisión. Está fuera de tema. – mydogisbox

Respuesta

23

Entonces, después de todo eso, ¿recomendaría esta estructura de diseño en una aplicación ? y por qué.

No.

Es un código muy bueno; Particularmente me gusta el uso de imp_implementationWithBlock() (pero admito que podría ser parcial a esa característica particular del tiempo de ejecución;). Y, por supuesto, exploraciones como esta son siempre una herramienta de aprendizaje increíblemente valiosa. El problema, en el contexto del uso del "proyecto de pago en el mundo real", es que está creando efectivamente un puente relativamente genérico que tendrá que tener puentes específicos en cada extremo para interactuar con bibliotecas típicas de C++ o con el objetivo típico. -C APIs/bibliotecas. Para decirlo de otra manera, ha creado efectivamente un nuevo tiempo de ejecución derivado de una amalgama de dos tiempos de ejecución existentes.

Y, como señalas en los contras, tienes que tocar, ajustar, modificar y/o depurar una cuña encima de cada clase de C++ que quieras incorporar a este patrón.

Al trabajar con un poco de código Objective-C++ en los últimos 20 años, un puente como este suele ser más problemático de lo que vale. Probablemente sería mejor, pasar menos tiempo escribiendo y depurando código, creando envoltorios simples de Objective-C alrededor de las API de C++ (o C, francamente) que luego pueden integrarse y consumirse con los framework Objective-C del sistema de destino.

Cuestiones relacionadas