Anda dapat mencampur C ++ dengan Objective-C jika Anda melakukannya dengan hati-hati. Ada beberapa peringatan tetapi secara umum mereka dapat dicampur. Jika Anda ingin memisahkannya, Anda dapat mengatur fungsi pembungkus C standar yang memberikan objek Objective-C antarmuka gaya-C yang dapat digunakan dari kode non-Objective-C (pilih nama yang lebih baik untuk file Anda, saya telah memilih nama-nama ini untuk verbositas):
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);
}
Fungsi pembungkus tidak perlu berada dalam .m
file yang sama dengan kelas Objective-C, tetapi file yang ada di dalamnya perlu dikompilasi sebagai kode Objective-C . Header yang mendeklarasikan fungsi wrapper harus disertakan dalam kode CPP dan Objective-C.
(CATATAN: jika file implementasi Objective-C diberi ekstensi ".m" itu tidak akan terhubung di bawah Xcode. Ekstensi ".mm" memberitahu Xcode untuk mengharapkan kombinasi Objective-C dan C ++, yaitu Objective-C ++. )
Anda dapat mengimplementasikan di atas dengan cara Berorientasi Objek dengan menggunakan idiom PIMPL . Implementasinya hanya sedikit berbeda. Singkatnya, Anda menempatkan fungsi pembungkus (dideklarasikan dalam "MyObject-C-Interface.h") di dalam kelas dengan penunjuk kosong (pribadi) ke sebuah instance dari 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
Perhatikan bahwa metode pembungkus tidak lagi memerlukan penunjuk kosong ke instance MyClass; sekarang menjadi anggota pribadi MyClassImpl. Metode init digunakan untuk membuat instance 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
Perhatikan bahwa MyClass dibuat dengan panggilan ke MyClassImpl :: init. Anda bisa membuat instance MyClass di konstruktor MyClassImpl, tetapi itu umumnya bukan ide yang bagus. Instance MyClass dihancurkan dari destruktor MyClassImpl. Seperti dengan implementasi C-style, metode pembungkus hanya tunduk pada metode MyClass masing-masing.
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." );
}
}
Anda sekarang mengakses panggilan ke MyClass melalui implementasi pribadi MyClassImpl. Pendekatan ini dapat menguntungkan jika Anda mengembangkan aplikasi portabel; Anda dapat dengan mudah menukar implementasi MyClass dengan yang satu spesifik ke platform lain ... tapi jujur, apakah ini implementasi yang lebih baik lebih merupakan masalah selera dan kebutuhan.
extern "C"
sebelumint MyObjectDoSomethingWith
Anda dapat mengompilasi kode Anda sebagai Objective-C ++ - cara termudah adalah dengan mengganti nama .cpp Anda menjadi .mm. Ini kemudian akan dikompilasi dengan benar jika Anda menyertakan
EAGLView.h
(Anda mendapatkan begitu banyak kesalahan karena kompiler C ++ tidak memahami salah satu kata kunci spesifik Objective-C), dan Anda dapat (untuk sebagian besar) mencampur Objective-C dan C ++ bagaimanapun Anda Suka.sumber
Solusi termudah adalah dengan memberi tahu Xcode untuk mengkompilasi semuanya sebagai Objective C ++.
Setel project atau setelan target Anda untuk Compile Sources As ke Objective C ++ dan kompilasi ulang.
Kemudian Anda dapat menggunakan C ++ atau Objective C di mana saja, misalnya:
Ini memiliki pengaruh yang sama seperti mengganti nama semua file sumber Anda dari .cpp atau .m menjadi .mm.
Ada dua kelemahan kecil untuk ini: clang tidak dapat menganalisis kode sumber C ++; beberapa kode C yang relatif aneh tidak dapat dikompilasi di bawah C ++.
sumber
Langkah 1
Buat file c tujuan (file .m) dan file header yang sesuai.
// File header (Kami menyebutnya "ObjCFunc.h")
// File C Tujuan yang sesuai (Kami menyebutnya "ObjCFunc.m")
Langkah 2
Sekarang kita akan mengimplementasikan fungsi c ++ untuk memanggil fungsi tujuan c yang baru saja kita buat! Jadi untuk itu kita akan mendefinisikan file .mm dan file header yang sesuai (file ".mm" akan digunakan di sini karena kita akan dapat menggunakan pengkodean Objective C dan C ++ dalam file)
// File header (Kami menyebutnya "ObjCCall.h")
// File C ++ Objective yang sesuai (Kami menyebutnya "ObjCCall.mm")
LANGKAH 3
Memanggil fungsi c ++ (yang sebenarnya memanggil metode tujuan c)
//Panggilan terakhir
Semoga berhasil!
sumber
Anda perlu membuat file C ++ Anda diperlakukan sebagai Objective-C ++. Anda dapat melakukan ini di xcode dengan mengganti nama foo.cpp menjadi foo.mm (.mm adalah ekstensi obj-c ++). Kemudian seperti yang dikatakan orang lain, sintaks perpesanan standar obj-c akan berfungsi.
sumber
Terkadang mengganti nama .cpp menjadi .mm bukanlah ide yang baik, terutama jika proyek bersifat lintas platform. Dalam hal ini untuk proyek xcode saya membuka file proyek xcode melalui TextEdit, menemukan string yang berisi file yang menarik, seharusnya seperti:
dan kemudian ubah jenis file dari sourcecode.cpp.cpp menjadi sourcecode.cpp.objcpp
Ini setara dengan mengganti nama .cpp menjadi .mm
sumber
Selain itu, Anda dapat memanggil runtime Objective-C untuk memanggil metode tersebut.
sumber
Jawaban @ DawidDrozd di atas luar biasa.
Saya akan menambahkan satu poin. Versi terbaru dari compiler Clang mengeluh tentang perlunya "bridging cast" jika mencoba menggunakan kodenya.
Ini tampaknya masuk akal: menggunakan trampolin menciptakan bug potensial: karena kelas Objective-C adalah referensi yang dihitung, jika kita meneruskan alamatnya sebagai void *, kita berisiko mengalami penunjuk yang menggantung jika kelas tersebut dikumpulkan sampah saat callback masih aktif.
Solusi 1) Kakao menyediakan fungsi makro CFBridgingRetain dan CFBridgingRelease yang mungkin menambah dan mengurangi satu dari jumlah referensi objek Objective-C. Oleh karena itu, kita harus berhati-hati dengan beberapa callback, untuk merilis jumlah yang sama seperti yang kita pertahankan.
Solusi 2) Alternatifnya adalah dengan menggunakan ekuivalen dari referensi lemah (mis. Tidak ada perubahan pada jumlah penahan), tanpa keamanan tambahan.
Bahasa Objective-C menyediakan __bridge cast qualifier untuk melakukan ini (CFBridgingRetain dan CFBridgingRelease tampaknya menjadi pembungkus Kakao tipis di atas konstruksi bahasa Objective-C __bridge_retained dan rilis masing-masing, tetapi Cocoa tampaknya tidak memiliki padanan untuk __bridge).
Perubahan yang dibutuhkan adalah:
sumber
self
ke dalam closure, yang bisa menjadi usang. Hal lainnya adalah tidak jelas bagaimana hal ini berinteraksi dengan penghitungan referensi otomatis dan apakah compiler dapat mengetahui apa yang sedang terjadi. Dalam praktiknya saya tidak berhasil membuat situasi di mana salah satu versi gagal dalam contoh mainan satu modul sederhana.Anda dapat mencampur C ++ dengan Objectiv-C (Objective C ++). Tulis metode C ++ di kelas Objective C ++ Anda yang hanya memanggil
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];
dan memanggilnya dari C ++.Saya belum pernah mencobanya sebelumnya, tetapi mencobanya, dan membagikan hasilnya kepada kami.
sumber