2012-08-30 2 views
12

ViewController는 objective-C에 있고 대부분의 코드는 C++ (.mm)입니다. obj-c (C++에서) 멤버 함수에 콜백을 설치하고 C++에서 콜백하고 싶습니다. 이런 식으로 뭔가 (이 매우 simplifyed 것) :콜백에서 C++을 객관적인 코드로 변환

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

@implementation MyClass 

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

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

과 :

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

이 과정이 올바르지 않습니다. 어떤 충고라도 어떻게 할 수 있습니까?

답변

15

몇 가지 옵션이 있습니다.

사용하여 블록

당신은 당신의 콜백 작업을 전달하기 위해 블록을 사용할 수 있습니다. 콜백 "함수"에 매개 변수를 전달하지 않고도 코드를 호출 할 수 있으므로 가장 간단한 솔루션 일 것입니다. 블록은 C와 Clang의 모든 상위 집합에서 작동하며 Clang ++는 블록과 람다간에 암시 적 캐스트를 허용합니다. 대신 함수 포인터의 펑 (또는 블록이 간단 경우)을 허용하도록 C++ 끝에 약간의 재 작업이 필요할 수 있습니다

#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]; 
    }); 
} 

.

블록이 클로저를 생성하기 때문에 이러한 종류의 작업에 매우 편리합니다.

블록은 C, C++ 및 Objective-C에 대한 Apple 확장 프로그램입니다. 그들에 대해 더 자세히보기 here.

당신이 당신의 선택의 함수 포인터를 액세스 할 수

사용 목적-C 런타임을 호출 할 메서드에 함수 포인터를 획득하기 위해 오브젝티브 C 런타임을 사용합니다. 이것은 더 지루하고 세 변수 (메서드를 호출 할 개체, 사용할 선택기 및 메서드 구현)를 추적해야하지만 실제로 Objective-C를 사용할 수없는 경우에도 작동합니다 통사론.

목표 - C 메소드 구현이 서명 함수 포인터 : self 당신이 기대하는 것입니다

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

_cmd이 메서드 호출을 발생하는 선택입니다합니다 (_cmd 변수는 모두 실제로 볼 수 있습니다 Objective-C 방법, 시도해보십시오) 나머지는 가변적 인 것으로 간주됩니다. IMP 변수를 적절한 함수 시그니처에 캐스팅해야합니다. 가변 C 함수의 호출 규칙이 Objective-C 메서드 호출의 호출 규칙과 항상 일치하지 않기 때문입니다. Objective-C 메서드 호출은 사용자의 표준 함수 호출 규칙입니다. 컴파일러, 아마도 cdecl 또는 amd64 호출 규칙이며 가변적 호출 규칙은 이 아니며 항상 같은 것입니다. reinterpret_cast이이를 수행 할 수 있습니다.

다음은 비슷한 의도로 작성된 코드입니다. 적절한 함수 서명을 얻는 데 도움이되는 C++ 11 가변 템플릿을 사용합니다. nil 검사가 목표 - C 런타임에 의해 처리되기 때문에

#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)); 
} 

또한, 인스턴스는 함수 호출을 사용하기 전에하지 nil입니다주의해야합니다. 이 경우 우회합니다. 객체의

보관할 트랙과 SEL

사용 -[NSObject performSelector:]는 콜백을 수행 할 수 있습니다. 기본적으로 Objective-C 런타임 솔루션의보다 간단한 버전입니다.

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)); 
} 

++ 기능

는 C 내부 전화를 포장 나는이 사람이 정말 어떤 예제를 필요로하지 않는다 생각합니다. 첫 번째 매개 변수로 객체 유형을 허용하고 원하는 메소드를 호출하는 함수를 만듭니다. SEL 솔루션과 마찬가지로 호출 할 함수와 호출 할 개체를 개별적으로 추적해야합니다.

+2

구현을받을 필요가 없습니다. – user102008

+0

구현 대신 항상'objc_msgSend'를 사용할 수 있습니다. 이 경우 메서드 포인터 대신 선택기를 사용합니다. – zneak

+0

아니요. IMP를 사용하면 선택기를 가지고 있어야합니다. 이제는 IMP를 얻을 필요가 없다는 점을 제외하면 동일합니다. – user102008

1

C++ 코드 내에서 Objective-C 콜백을 호출하는 것이 가능하지 않다고 생각합니다. 다른 방법으로도 가능합니다. 나는이 문제를했을 때 내가 무슨 짓을

는 목표 - C 영원히 실행하는 EVENT_LOOP을 쓰기 및 이벤트에게 C++ std::vector 인스턴스에서를 얻는 것입니다. 그런 식으로 C++에서 Objective-C로 콜백을 "호출"하고 싶다면 이벤트를 해당 std::vector에 추가하기 만하면됩니다.

1

Obj-C와 C++을 .mm 파일에 자유롭게 혼합 할 수 있습니다.이 파일을 "Objective-C++"라고합니다.

C++ 코드와 Objective-C 코드의 격리를 유지하려면 C++ 콜백 내에서 Objective-C 메서드 호출을 (보통 방식으로) 만들 수있는 가벼운 외관을 만들 수 있습니다.

2

C++과 Obj-C는 모두 C의 상위 집합이므로 C 함수에 대한 C++ 코드 콜백을 사용할 수 있습니다. 콜백을 다시 Obj-C 객체에 전달하려면 C 콜백 인수에 원래 Obj-C 객체에 대한 포인터가 포함되어 있는지 확인하십시오.

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

당신이 보여주고있는 캐스트를하려면 무료 전화 브리징으로해야합니다 : '[((__bridge MYObjCClassType *) classInstance) exampleCallback]; – dadude999