Vous pouvez mélanger C++ et Objective-C si vous le faites avec soin. Il y a quelques réserves, mais en général, on peut les mélanger. Si vous voulez les garder séparés, vous pouvez mettre en place une fonction wrapper C standard qui donne à l'objet Objective-C une interface de style C utilisable depuis du code non-Objective-C (choisissez de meilleurs noms pour vos fichiers, j'ai choisi ces noms pour la verbosité) :
MyObject-C-Interface.h
#ifndef __MYOBJECT_C_INTERFACE_H__
#define __MYOBJECT_C_INTERFACE_H__
// This is the C "trampoline" function that will be used
// to invoke a specific Objective-C method FROM C++
int MyObjectDoSomethingWith (void *myObjectInstance, void *parameter);
#endif
MyObject.h
#import "MyObject-C-Interface.h"
// An Objective-C class that needs to be accessed from C++
@interface MyObject : NSObject
{
int someVar;
}
// The Objective-C member function you want to call from C++
- (int) doSomethingWith:(void *) aParameter;
@end
MonObjet.mm
#import "MyObject.h"
@implementation MyObject
// C "trampoline" function to invoke Objective-C method
int MyObjectDoSomethingWith (void *self, void *aParameter)
{
// Call the Objective-C method using Objective-C syntax
return [(id) self doSomethingWith:aParameter];
}
- (int) doSomethingWith:(void *) aParameter
{
// The Objective-C function you wanted to call from C++.
// do work here..
return 21 ; // half of 42
}
@end
MyCPPClass.cpp
#include "MyCPPClass.h"
#include "MyObject-C-Interface.h"
int MyCPPClass::someMethod (void *objectiveCObject, void *aParameter)
{
// To invoke an Objective-C method from C++, use
// the C trampoline function
return MyObjectDoSomethingWith (objectiveCObject, aParameter);
}
La fonction wrapper n'a pas besoin pour être dans le même .m
comme la classe Objective-C, mais le fichier dans lequel elle existe est le suivant doit être compilé en code Objective-C . L'en-tête qui déclare la fonction wrapper doit être inclus dans le code CPP et Objective-C.
(NOTE : si le fichier d'implémentation Objective-C porte l'extension ".m", il ne sera pas lié sous Xcode. L'extension ".mm" indique à Xcode de s'attendre à une combinaison d'Objective-C et de C++, c'est-à-dire d'Objective-C++).
Vous pouvez mettre en œuvre ce qui précède d'une manière orientée objet en utilisant la fonction idiome PIMPL . La mise en œuvre n'est que légèrement différente. En bref, vous placez les fonctions d'encapsulation (déclarées dans "MyObject-C-Interface.h") à l'intérieur d'une classe avec un pointeur (privé) void vers une instance de MyClass.
MyObject-C-Interface.h (PIMPL)
#ifndef __MYOBJECT_C_INTERFACE_H__
#define __MYOBJECT_C_INTERFACE_H__
class MyClassImpl
{
public:
MyClassImpl ( void );
~MyClassImpl( void );
void init( void );
int doSomethingWith( void * aParameter );
void logMyMessage( char * aCStr );
private:
void * self;
};
#endif
Remarquez que les méthodes wrapper ne nécessitent plus le pointeur void vers une instance de MyClass ; il est maintenant un membre privé de MyClassImpl. La méthode init est utilisée pour instancier une instance de MyClass ;
MyObject.h (PIMPL)
#import "MyObject-C-Interface.h"
@interface MyObject : NSObject
{
int someVar;
}
- (int) doSomethingWith:(void *) aParameter;
- (void) logMyMessage:(char *) aCStr;
@end
MonObjet.mm (PIMPL)
#import "MyObject.h"
@implementation MyObject
MyClassImpl::MyClassImpl( void )
: self( NULL )
{ }
MyClassImpl::~MyClassImpl( void )
{
[(id)self dealloc];
}
void MyClassImpl::init( void )
{
self = [[MyObject alloc] init];
}
int MyClassImpl::doSomethingWith( void *aParameter )
{
return [(id)self doSomethingWith:aParameter];
}
void MyClassImpl::logMyMessage( char *aCStr )
{
[(id)self doLogMessage:aCStr];
}
- (int) doSomethingWith:(void *) aParameter
{
int result;
// ... some code to calculate the result
return result;
}
- (void) logMyMessage:(char *) aCStr
{
NSLog( aCStr );
}
@end
Remarquez que MyClass est instanciée par un appel à MyClassImpl::init. Vous pourriez instancier MyClass dans le constructeur de MyClassImpl, mais ce n'est généralement pas une bonne idée. L'instance de MyClass est détruite par le destructeur de MyClassImpl. Comme dans l'implémentation de style C, les méthodes du wrapper se réfèrent simplement aux méthodes respectives de MyClass.
MyCPPClass.h (PIMPL)
#ifndef __MYCPP_CLASS_H__
#define __MYCPP_CLASS_H__
class MyClassImpl;
class MyCPPClass
{
enum { cANSWER_TO_LIFE_THE_UNIVERSE_AND_EVERYTHING = 42 };
public:
MyCPPClass ( void );
~MyCPPClass( void );
void init( void );
void doSomethingWithMyClass( void );
private:
MyClassImpl * _impl;
int _myValue;
};
#endif
MyCPPClass.cpp (PIMPL)
#include "MyCPPClass.h"
#include "MyObject-C-Interface.h"
MyCPPClass::MyCPPClass( void )
: _impl ( NULL )
{ }
void MyCPPClass::init( void )
{
_impl = new MyClassImpl();
}
MyCPPClass::~MyCPPClass( void )
{
if ( _impl ) { delete _impl; _impl = NULL; }
}
void MyCPPClass::doSomethingWithMyClass( void )
{
int result = _impl->doSomethingWith( _myValue );
if ( result == cANSWER_TO_LIFE_THE_UNIVERSE_AND_EVERYTHING )
{
_impl->logMyMessage( "Hello, Arthur!" );
}
else
{
_impl->logMyMessage( "Don't worry." );
}
}
Vous accédez désormais aux appels à MyClass via une implémentation privée de MyClassImpl. Cette approche peut être avantageuse si vous développez une application portable ; vous pouvez simplement remplacer l'implémentation de MyClass par une implémentation spécifique à l'autre plateforme ... mais honnêtement, la question de savoir s'il s'agit d'une meilleure implémentation est plus une question de goût et de besoins.