Sie können C ++ mit Objective-C mischen, wenn Sie dies sorgfältig tun. Es gibt einige Einschränkungen, aber im Allgemeinen können sie gemischt werden. Wenn Sie sie getrennt halten möchten, können Sie eine Standard-C-Wrapper-Funktion einrichten, die dem Objective-C-Objekt eine verwendbare C-artige Schnittstelle aus Nicht-Objective-C-Code verleiht (wählen Sie bessere Namen für Ihre Dateien, ich habe diese Namen ausgewählt für Ausführlichkeit):
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
MyObject.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);
}
Die Wrapper-Funktion muss sich nicht in derselben .m
Datei wie die Objective-C-Klasse befinden, aber die Datei, in der sie vorhanden ist, muss als Objective-C-Code kompiliert werden . Der Header, der die Wrapper-Funktion deklariert, muss sowohl im CPP- als auch im Objective-C-Code enthalten sein.
(HINWEIS: Wenn die Objective-C-Implementierungsdatei die Erweiterung ".m" erhält, wird sie nicht unter Xcode verknüpft. Die Erweiterung ".mm" weist Xcode an, eine Kombination aus Objective-C und C ++ zu erwarten, dh Objective-C ++. )
Sie können das Obige objektorientiert implementieren, indem Sie das PIMPL-Idiom verwenden . Die Implementierung ist nur geringfügig anders. Kurz gesagt, Sie platzieren die Wrapper-Funktionen (deklariert in "MyObject-C-Interface.h") in einer Klasse mit einem (privaten) Void-Zeiger auf eine Instanz von 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
Beachten Sie, dass die Wrapper-Methoden den void-Zeiger auf eine Instanz von MyClass nicht mehr benötigen. Es ist jetzt ein privates Mitglied von MyClassImpl. Die init-Methode wird verwendet, um eine MyClass-Instanz zu instanziieren.
MyObject.h (PIMPL)
#import "MyObject-C-Interface.h"
@interface MyObject : NSObject
{
int someVar;
}
- (int) doSomethingWith:(void *) aParameter;
- (void) logMyMessage:(char *) aCStr;
@end
MyObject.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
Beachten Sie, dass MyClass mit einem Aufruf von MyClassImpl :: init instanziiert wird. Sie könnten MyClass im Konstruktor von MyClassImpl instanziieren, aber das ist im Allgemeinen keine gute Idee. Die MyClass-Instanz wird vom Destruktor von MyClassImpl zerstört. Wie bei der Implementierung im C-Stil verschieben sich die Wrapper-Methoden einfach auf die jeweiligen Methoden von 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." );
}
}
Sie können jetzt über eine private Implementierung von MyClassImpl auf Aufrufe von MyClass zugreifen. Dieser Ansatz kann vorteilhaft sein, wenn Sie eine tragbare Anwendung entwickeln. Sie könnten einfach die Implementierung von MyClass gegen eine für die andere Plattform spezifische austauschen ... aber ehrlich gesagt ist es eher eine Frage des Geschmacks und der Bedürfnisse, ob dies eine bessere Implementierung ist.
extern "C"
vor demint MyObjectDoSomethingWith
Sie können Ihren Code als Objective-C ++ kompilieren. Am einfachsten ist es, Ihre .cpp in .mm umzubenennen. Es wird dann ordnungsgemäß kompiliert, wenn Sie einschließen
EAGLView.h
(Sie haben so viele Fehler erhalten, weil der C ++ - Compiler keines der Objective-C-spezifischen Schlüsselwörter verstanden hat), und Sie können Objective-C und C ++ (größtenteils) nach Belieben mischen mögen.quelle
Die einfachste Lösung besteht darin, Xcode anzuweisen, alles als Objective C ++ zu kompilieren.
Legen Sie Ihre Projekt- oder Zieleinstellungen für Compile Sources As für Objective C ++ fest und kompilieren Sie sie neu.
Dann können Sie C ++ oder Objective C überall verwenden, zum Beispiel:
Dies hat den gleichen Effekt wie das Umbenennen aller Quelldateien von .cpp oder .m in .mm.
Dies hat zwei kleine Nachteile: clang kann C ++ - Quellcode nicht analysieren; Einige relativ seltsame C-Codes werden unter C ++ nicht kompiliert.
quelle
Schritt 1
Erstellen Sie eine objektive c-Datei (.m-Datei) und die entsprechende Header-Datei.
// Header-Datei (Wir nennen es "ObjCFunc.h")
// Entsprechende Objective C-Datei (Wir nennen es "ObjCFunc.m")
Schritt 2
Jetzt implementieren wir eine c ++ - Funktion, um die soeben erstellte objektive c-Funktion aufzurufen! Dazu definieren wir eine .mm-Datei und die zugehörige Header-Datei (".mm" -Datei ist hier zu verwenden, da wir sowohl Objective C- als auch C ++ - Codierung in der Datei verwenden können).
// Header-Datei (Wir nennen es "ObjCCall.h")
// Entsprechende Ziel-C ++ - Datei (Wir nennen sie "ObjCCall.mm")
Schritt 3
Aufruf der c ++ - Funktion (die tatsächlich die objektive c-Methode aufruft)
//Letzte Aufforderung
Hoffe das funktioniert!
quelle
Sie müssen dafür sorgen, dass Ihre C ++ - Datei als Objective-C ++ behandelt wird. Sie können dies in xcode tun, indem Sie foo.cpp in foo.mm umbenennen (.mm ist die obj-c ++ - Erweiterung). Dann wird, wie andere gesagt haben, die Standard-obj-c-Messaging-Syntax funktionieren.
quelle
Manchmal ist es nicht ratsam, .cpp in .mm umzubenennen, insbesondere wenn das Projekt plattformübergreifend ist. In diesem Fall für Xcode-Projekt öffne ich die Xcode-Projektdatei über TextEdit und habe eine Zeichenfolge gefunden, deren Inhaltsinteressendatei wie folgt aussehen sollte:
und ändern Sie dann den Dateityp von sourcecode.cpp.cpp in sourcecode.cpp.objcpp
Dies entspricht dem Umbenennen von .cpp in .mm
quelle
Sie können auch die Objective-C-Laufzeit aufrufen, um die Methode aufzurufen.
quelle
@ DawidDrozds Antwort oben ist ausgezeichnet.
Ich würde einen Punkt hinzufügen. Neuere Versionen des Clang-Compilers beklagen, dass beim Versuch, seinen Code zu verwenden, eine "Überbrückungsbesetzung" erforderlich ist.
Dies erscheint vernünftig: Die Verwendung eines Trampolins führt zu einem potenziellen Fehler: Da Objective-C-Klassen als Referenz gezählt werden und wir ihre Adresse als ungültig * weitergeben, besteht die Gefahr, dass ein hängender Zeiger angezeigt wird, wenn die Klasse während des Rückrufs als Müll gesammelt wird aktiv.
Lösung 1) Cocoa bietet die Makrofunktionen CFBridgingRetain und CFBridgingRelease, mit denen vermutlich eine vom Referenzzähler des Objective-C-Objekts addiert und subtrahiert wird. Wir sollten daher bei mehreren Rückrufen vorsichtig sein, um die gleiche Anzahl von Malen freizugeben, die wir behalten.
Lösung 2) Die Alternative besteht darin, das Äquivalent einer schwachen Referenz (dh keine Änderung der Aufbewahrungszahl) ohne zusätzliche Sicherheit zu verwenden.
Die Objective-C-Sprache bietet hierfür das __bridge-Cast-Qualifikationsmerkmal (CFBridgingRetain und CFBridgingRelease scheinen dünne Cocoa-Wrapper über den Objective-C-Sprachkonstrukten __bridge_retained bzw. release zu sein, Cocoa scheint jedoch kein Äquivalent für __bridge zu haben).
Die erforderlichen Änderungen sind:
quelle
self
in den Verschluss übergeben, die veraltet sein könnte. Der andere Punkt ist, dass nicht klar ist, wie dies mit der automatischen Referenzzählung interagiert und ob der Compiler herausfinden kann, was los ist. In der Praxis habe ich es nicht geschafft, eine Situation zu schaffen, in der eine der beiden Versionen in einem einfachen Ein-Modul-Spielzeugbeispiel fehlgeschlagen ist.Sie können C ++ mit Objectiv-C (Objective C ++) mischen. Schreiben Sie eine C ++ - Methode in Ihre Objective C ++ - Klasse, die einfach aufruft
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];
und von Ihrem C ++ aus .Ich habe es noch nie zuvor selbst ausprobiert, aber probieren Sie es aus und teilen Sie uns die Ergebnisse mit.
quelle