2012-08-30 11 views
12

Ich habe ViewController in objective-c und die meisten meiner Code ist C++ (. Mmm). Ich möchte einige Callbacks zu den Mitgliedsfunktionen von obj-c (in C++) herstellen und sie von C++ benennen. So etwas wie dies (es ist sehr simplifyed):Rückruf von C++ zu Ziel c

@interface MyClass 
{ } 
-(void)my_callback; 
@end 

@implementation MyClass 

-(void)my_callback 
{ 
    printf("called!\n"); 
} 

-(void)viewDidLoad 
{ 
    // setup_callback("to my_callback ?"); 
} 
@end 

und:

void setup_callback(void(*func)()) { func(); } 

dies nicht korrekt ist natürlich. Irgendein Rat, wie kann ich es bitte tun?

Antwort

15

Sie haben ein paar Optionen.

Mit Blöcke

können Sie Blöcke benutzen, um Ihre Rückruf Arbeit zu vermitteln. Dies ist wahrscheinlich die einfachste Lösung, da Sie Ihren Code aufrufen können, ohne einen Parameter an die Callback- "Funktion" übergeben zu müssen. Blöcke arbeiten in C und alle ihre Supersätze mit Clang, und Clang ++ erlaubt sogar implizite Umwandlungen zwischen Blöcken und Lambdas.

#include <dispatch/dispatch.h> 

void setup_callback(dispatch_block_t block) 
{ 
    // required to copy the block to the heap, otherwise it's on the stack 
    dispatch_block_t copy = [block copy]; 

    // setup stuff here 
    // when you want to call the callback, do as if it was a function pointer: 
    // block(); 
} 

int main() 
{ 
    MyClass* instance = [[MyClass alloc] init]; 

    setup_callback(^{ 
     [instance callback_method]; 
    }); 
} 

, dass einige Nacharbeiten an dem C++ Ende erfordern könnte functors (oder nur blockiert, wenn es einfacher ist) zu akzeptieren, anstatt Funktionszeiger.

Da Blöcke Schließungen erzeugen, sind sie sehr praktisch für diese Art von Arbeiten.

Blöcke sind eine Apple-Erweiterung für C, C++ und Objective-C. Sehen Sie mehr über sie .

Verwenden Sie die Objective-C-Laufzeit der Funktionszeiger auf die Methode, die Sie

Verwenden Sie die Objective-C-Laufzeit aufgerufen werden sollen erwerben die Funktionszeiger Ihrer Wähler zuzugreifen. Dies ist mühsamer und erfordert, dass Sie drei Variablen (das Objekt, für das die Methode aufgerufen wird, den zu verwendenden Selektor und die Methodenimplementierung) im Auge behalten, aber es funktioniert sogar, wenn Sie das Objective-C nicht verwenden können Syntax.

Objective-C-Methode Implementierungen Funktionszeiger mit dieser Signatur sind:

typedef void (*IMP)(id self, SEL _cmd, ...); 

Wo self ist das, was man erwarten würde, _cmd ist der Wähler, die diese Methode Aufruf verursacht (die _cmd Variable ist in allen tatsächlich verfügbar Objective-C-Methoden, versuchen Sie es), und der Rest gilt als variadic. Sie müssenIMP Variablen in die richtige Funktion Signatur umwandeln, da die Aufrufkonvention für variadic C-Funktionen nicht immer die aufrufende Konvention für Objective-C-Methodenaufrufe (die Objective-C-Methode Aufruf ist die Standardfunktion Aufruf Konvention für Ihre Compiler, wahrscheinlich entweder cdecl oder die Aufrufkonvention amd64, und die variadic Aufrufkonvention ist nicht immer das gleiche). A reinterpret_cast wird dazu in der Lage sein.

Hier ist ein Code, den ich für ähnliche Absichten zusammengestellt habe. Es verwendet C++ 11 variadische Vorlagen, um die korrekte Funktionssignatur zu erhalten.

#include <objc/runtime.h> 

template<typename TReturnType, typename... TArguments> 
auto GetInstanceMethodPointer(Class class, SEL selector) -> TReturnType (*)(id, SEL, TArguments...) 
{ 
    Method m = class_getInstanceMethod(class, selector); 
    IMP imp = method_getImplementation(m); 
    return reinterpret_cast<TReturnType (*)(id, SEL, TArguments...)>(imp); 
} 

int main() 
{ 
    MyClass* instance = [[MyClass alloc] init]; 
    auto foo = GetInstanceMethodPointer<void>(
     [MyClass class], 
     @selector(my_callback)); 
    // foo is a void (*)(id, SEL) function pointer 
    foo(instance, @selector(my_callback)); 
} 

Nehmen Sie auch darauf, dass Ihr Beispiel ist nicht nil vor dem Funktionsaufruf zu verwenden, da nil Prüfung durch die Objective-C-Laufzeit behandelt wird. In diesem Fall umgehen wir es.

Behalten eines Objekts und ein SEL

Verwenden -[NSObject performSelector:] Ihren Rückruf auszuführen. Im Grunde eine einfachere Version der Objective-C-Laufzeitlösung.

void setup_callback(id object, SEL selector) 
{ 
    // do stuff 
    // to execute the callback: 
    // [object performSelector:selector]; 
} 

int main() 
{ 
    MyClass* instance = [[MyClass alloc] init]; 

    setup_callback(instance, @selector(my_callback)); 
} 

Wrapping Ihren Anruf innerhalb einer C++ Funktion

Ich denke, das man nicht wirklich kein Beispiel braucht. Erstellen Sie eine Funktion, die Ihren Objekttyp als ersten Parameter akzeptiert, und rufen Sie die gewünschte Methode auf. Ähnlich wie bei der SEL Lösung müssen Sie dann separat die zu rufende Funktion und das Objekt, auf dem sie aufgerufen werden, verfolgen.

+2

Sie brauchen nicht einmal die Implementierung zu bekommen. Sie können immer 'objc_msgSend' anstelle der Implementierung – user102008

+0

True verwenden. In diesem Fall würden Sie den Selektor anstelle des Methodenzeigers verwenden. – zneak

+0

Nein. Mit Ihrem IMP müssten Sie den Selektor trotzdem tragen. Es ist also dasselbe, nur dass Sie jetzt kein IMP benötigen. – user102008

1

Ich glaube nicht, dass das möglich ist: Aufrufen eines Objective-C-Callbacks innerhalb von C++ - Code. Andersherum ist möglich.

Was ich tat, als ich dieses Problem hatte, ist ein EVENT_LOOP läuft immer in Objective-C zu schreiben, und nutzen Sie die Ereignisse aus einer C++ std::vector Instanz. Wenn Sie in C++ einen Callback in Objective-C "aufrufen" möchten, fügen Sie das Ereignis einfach zu der entsprechenden std::vector hinzu.

1

Sie können Obj-C und C++ in Ihren .mm-Dateien frei mischen - es wird als "Objective-C++" bezeichnet.

Um die Isolierung von C++ - Code und Objective-C-Code beizubehalten, könnten Sie eine Lightweight-Fassade zwischen ihnen erstellen, die Aufrufe von Objective-C-Methoden innerhalb von C++ - Callbacks aufruft.

2

Da C++ und Obj-C beide Obermengen von C sind, können Sie Ihren C++ - Code-Rückruf zu einer C-Funktion haben. Um den Rückruf zurück zu Ihrem Obj-C-Objekt zu kommunizieren, stellen Sie einfach sicher, dass die C-Callback-Argumente einen Zeiger auf das ursprüngliche Obj-C-Objekt enthalten.

// C++ calls this C function 
void exampleCallback(void* classInstance) 
{ 
    // callback arguments include a pointer to the Obj-C object 
    [((MYObjCClassType*)classInstance) exampleCallback]; 
} 
+0

Um das zu tun, zeigen Sie, müssen Sie es mit gebührenfreien Bridging tun: '[((__bridge MYObjCClassType *) classInstance) BeispielCallback];' – dadude999