Możesz mieszać C ++ z Objective-C, jeśli robisz to ostrożnie. Jest kilka zastrzeżeń, ale ogólnie można je mieszać. Jeśli chcesz je zachować osobno, możesz ustawić standardową funkcję opakowującą C, która daje obiektowi Objective-C użyteczny interfejs w stylu C z kodu innego niż Objective-C (wybierz lepsze nazwy dla swoich plików, wybrałem te nazwy dla szczegółowości):
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);
}
Funkcja opakowania nie musi znajdować się w tym samym .m
pliku co klasa Objective-C, ale plik, w którym istnieje, musi być skompilowany jako kod Objective-C . Nagłówek, który deklaruje funkcję opakowania, musi być zawarty zarówno w kodzie CPP, jak i Objective-C.
(UWAGA: jeśli plik implementacji Objective-C ma rozszerzenie „.m”, nie będzie łączył się z Xcode. Rozszerzenie „.mm” mówi Xcode, aby oczekiwał kombinacji Objective-C i C ++, tj. Objective-C ++. )
Możesz zaimplementować powyższe w sposób zorientowany obiektowo, używając idiomu PIMPL . Implementacja jest tylko trochę inna. Krótko mówiąc, umieszczasz funkcje opakowujące (zadeklarowane w „MyObject-C-Interface.h”) wewnątrz klasy z (prywatnym) wskaźnikiem void do instancji 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
Zwróć uwagę, że metody opakowujące nie wymagają już wskaźnika void do instancji MyClass; jest teraz prywatnym członkiem MyClassImpl. Metoda init służy do tworzenia instancji MyClass;
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
Zwróć uwagę, że MyClass jest tworzony przez wywołanie MyClassImpl :: init. Możesz utworzyć wystąpienie MyClass w konstruktorze MyClassImpl, ale generalnie nie jest to dobry pomysł. Instancja MyClass jest niszczona z destruktora MyClassImpl. Podobnie jak w przypadku implementacji w stylu C, metody opakowujące po prostu odnoszą się do odpowiednich metod 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." );
}
}
Masz teraz dostęp do wywołań MyClass poprzez prywatną implementację MyClassImpl. Takie podejście może być korzystne, jeśli tworzysz aplikację przenośną; możesz po prostu zamienić implementację MyClass na specyficzną dla drugiej platformy ... ale szczerze mówiąc, to, czy jest to lepsza implementacja, jest bardziej kwestią gustu i potrzeb.
extern "C"
przedint MyObjectDoSomethingWith
Możesz skompilować swój kod jako Objective-C ++ - najprostszym sposobem jest zmiana nazwy pliku .cpp na .mm. Następnie skompiluje się poprawnie, jeśli dołączysz
EAGLView.h
(otrzymywało się tak wiele błędów, ponieważ kompilator C ++ nie zrozumiał żadnego ze słów kluczowych specyficznych dla celu C) i możesz (w większości) mieszać Objective-C i C ++, jakkolwiek lubić.źródło
Najłatwiejszym rozwiązaniem jest po prostu powiedzenie Xcode, aby skompilował wszystko jako Objective C ++.
Ustaw projekt lub ustawienia docelowe dla Skompiluj źródła jako cel C ++ i ponownie skompiluj.
Wtedy możesz używać C ++ lub Objective C wszędzie, na przykład:
Ma to taki sam wpływ, jak zmiana nazw wszystkich plików źródłowych z .cpp lub .m na .mm.
Ma to dwie drobne wady: clang nie może analizować kodu źródłowego C ++; jakiś stosunkowo dziwny kod w C nie kompiluje się w C ++.
źródło
Krok 1
Utwórz plik C celu (plik .m) i odpowiadający mu plik nagłówkowy.
// Plik nagłówkowy (nazywamy go „ObjCFunc.h”)
// Odpowiedni plik celu C (nazywamy go „ObjCFunc.m”)
Krok 2
Teraz zaimplementujemy funkcję c ++, aby wywołać funkcję celu c, którą właśnie utworzyliśmy! W tym celu zdefiniujemy plik .mm i odpowiadający mu plik nagłówkowy (plik ".mm" ma być tutaj użyty, ponieważ będziemy mogli używać zarówno kodowania Objective C, jak i C ++ w pliku)
// Plik nagłówkowy (nazywamy go „ObjCCall.h”)
// Odpowiedni plik celu C ++ (nazywamy go „ObjCCall.mm”)
Krok 3
Wywołanie funkcji C ++ (która faktycznie wywołuje metodę celu c)
//Ostateczne wezwanie
Mam nadzieję, że to zadziała!
źródło
Musisz sprawić, by Twój plik C ++ był traktowany jako Objective-C ++. Możesz to zrobić w xcode, zmieniając nazwę foo.cpp na foo.mm (.mm to rozszerzenie obj-c ++). Wtedy, jak powiedzieli inni, zadziała standardowa składnia komunikatów obj-c.
źródło
Czasami zmiana nazwy .cpp na .mm nie jest dobrym pomysłem, zwłaszcza gdy projekt jest wieloplatformowy. W tym przypadku dla projektu xcode otwieram plik projektu xcode przez TextEdit, znalazłem ciąg, którego zawartość interesuje plik, powinien wyglądać następująco:
a następnie zmień typ pliku z sourcecode.cpp.cpp na sourcecode.cpp.objcpp
Jest to równoważne zmianie nazwy .cpp na .mm
źródło
Możesz również wywołać środowisko uruchomieniowe Objective-C, aby wywołać metodę.
źródło
Powyższa odpowiedź @ DawidDrozd jest doskonała.
Dodałbym jeden punkt. Najnowsze wersje kompilatora Clang narzekają na wymaganie „rzutowania pomostowego” przy próbie użycia jego kodu.
Wydaje się to rozsądne: użycie trampoliny stwarza potencjalny błąd: ponieważ klasy Objective-C są liczone jako referencje, jeśli przekażemy ich adres jako void *, ryzykujemy zawieszenie wskaźnika, jeśli klasa jest bezużyteczna, podczas gdy wywołanie zwrotne jest nadal aktywny.
Rozwiązanie 1) Kakao zapewnia funkcje makr CFBridgingRetain i CFBridgingRelease, które przypuszczalnie dodają i odejmują jedną z liczby referencji obiektu Objective-C. Dlatego powinniśmy być ostrożni w przypadku wielu wywołań zwrotnych, aby zwolnić taką samą liczbę razy, jaką zachowujemy.
Rozwiązanie 2) Alternatywą jest użycie odpowiednika słabego odniesienia (tj. Brak zmiany liczby zatrzymań) bez dodatkowego bezpieczeństwa.
Język Objective-C zapewnia kwalifikator rzutowania __bridge, aby to zrobić (CFBridgingRetain i CFBridgingRelease wydają się być cienkimi opakowaniami Cocoa na konstrukcjach języka Objective-C __bridge_retained i release, ale Cocoa nie wydaje się mieć odpowiednika dla __bridge).
Wymagane zmiany to:
źródło
self
do zamknięcia, które może stać się nieaktualne. Inną kwestią jest to, że nie jest jasne, jak współdziała to z automatycznym zliczaniem odwołań i czy kompilator może dowiedzieć się, co się dzieje. W praktyce nie udało mi się stworzyć sytuacji, w której którakolwiek z wersji zawodzi w prostym jednomodułowym przykładzie zabawki.Możesz mieszać C ++ z Objectiv-C (Objective C ++). Napisz metodę C ++ w swojej klasie Objective C ++, która po prostu wywołuje
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];
i wywołuje ją z Twojego C ++.Nie próbowałem tego wcześniej, ale daj mu szansę i podziel się z nami wynikami.
źródło